Analify Core
Overview
Three Core API domains have no external platform behind them — the data is Analify's own. They live under the same /api/core prefix and share its envelope and auth, but two conventions differ from the platform-mirror pages:
- Money here IS integer piasters (EGP × 100), matching the rest of the Analify API rather than a platform's unit.
- There is no "raw platform body" —
datais Analify's own shape, and it is stable.
Base paths: /api/core/cogs · /api/core/organization · /api/core/integrations
Every request: requires ?organizationId=<uuid>.
API Endpoints
| Endpoint | Scope | Description |
|---|---|---|
GET /cogs | finance.costs | List product costs |
POST /cogs | finance.costs | Create / update one SKU's cost |
GET /cogs/{sku} | finance.costs | Get one SKU's cost |
DELETE /cogs/{sku} | finance.costs | Remove a SKU's cost |
POST /cogs/bulk | finance.costs | Upsert many SKUs at once |
GET /cogs/coverage | finance.costs | How much of the catalogue is priced |
GET /organization | settings.general | Organization profile |
GET /organization/members | settings.members | Members and roles |
GET /organization/invitations | settings.members | Pending invitations |
GET /organization/roles | settings.general | The role registry |
GET /integrations | settings.integrations | Connected platforms and their state |
Product costs (COGS)
COGS is the input Analify cannot derive for you — and without it, profit is a guess. These endpoints are the programmatic way to keep it current (a nightly sync from your ERP, for example).
costPerUnit is an integer in piasters. 12500 means EGP 125.00, not EGP 12,500. Sending major units understates every cost 100× and silently inflates reported profit.
GET /api/core/cogs
GET /api/core/cogsParams: limit, offset, search, sort — one of sku · cost_asc · cost_desc · updated.
curl "https://analify.scalyax.ai/api/core/cogs?organizationId={organizationId}&limit=50&sort=cost_desc" \
-H "Authorization: Bearer {anlfy_token}"POST /api/core/cogs
POST /api/core/cogsUpserts one SKU. Body: sku, productName, costPerUnit (piasters, integer). All three are required — a missing field returns 400.
curl -X POST "https://analify.scalyax.ai/api/core/cogs?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{
"sku": "DEEP-COUPLES-AR",
"productName": "Deep Couples",
"costPerUnit": 12500
}'costPerUnit: 0 is valid and is stored, not treated as missing — a genuinely free item (a sample, a gift insert) is a real cost of zero. Only an absent or non-numeric field is rejected.
POST /api/core/cogs/bulk
POST /api/core/cogs/bulkBody: { items: [{ sku, productName, costPerUnit }] }. The array must be non-empty — an empty items returns 400 rather than succeeding as a no-op, because an empty bulk write is nearly always a caller bug.
curl -X POST "https://analify.scalyax.ai/api/core/cogs/bulk?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{
"items": [
{ "sku": "DEEP-COUPLES-AR", "productName": "Deep Couples", "costPerUnit": 12500 },
{ "sku": "DEEP-FAMILY-AR", "productName": "Deep Family", "costPerUnit": 9900 }
]
}'GET /api/core/cogs/{sku} and DELETE /api/core/cogs/{sku}
GET /api/core/cogs/{sku} and DELETE /api/core/cogs/{sku}A SKU with no cost on file returns 404 COGS_NOT_FOUND — distinct from a SKU priced at zero, which returns 200 with costPerUnit: 0.
GET /api/core/cogs/coverage
GET /api/core/cogs/coverageHow much of the catalogue actually has a cost. Read this before trusting a profit figure: coverage is the gate between "your profit is EGP 5,023" and the truth that nobody priced half the catalogue.
curl "https://analify.scalyax.ai/api/core/cogs/coverage?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"Prefer these endpoints over Shopify's variant-cost when you want costs in Analify without writing to the store. Use PATCH /api/core/shopify/products/variant-cost instead when Shopify should be the system of record.
Organization
GET /api/core/organization
GET /api/core/organizationName, slug, description, settings and timezone.
Billing state is deliberately stripped. credits and purchased_credits are removed from the response before it is serialized — an organization's spendable balance never leaves through this route. Read balances through the billing surfaces instead.
curl "https://analify.scalyax.ai/api/core/organization?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"A 404 ORG_NOT_FOUND means the id resolved to no organization.
GET /api/core/organization/members and …/invitations
GET /api/core/organization/members and …/invitationsCurrent members with their roles, and invitations still pending. Both carry personal data, so they sit on the tighter settings.members scope rather than settings.general.
GET /api/core/organization/roles
GET /api/core/organization/rolesThe role registry as defined in code — not org-scoped rows. The response is identical for every organization; it still requires organizationId, like every Core API route. Use it to render a role picker without hard-coding role names.
Integrations
GET /api/core/integrations
GET /api/core/integrationsWhich platforms this organization has connected, and the health of each. This is the cheapest discovery call in the whole API — a plain database read with no date range and no fan-out to any platform.
curl "https://analify.scalyax.ai/api/core/integrations?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"One row per connected account:
| Field | Meaning |
|---|---|
id | The integration UUID — what /api/analytics/integration/{id}/… routes take |
platform | meta_ads · tiktok_ads · google_ads · google_analytics · shopify · bosta … |
status | Connection state |
account_id, account_name | The account on the platform's side |
connect_type | How it was connected (OAuth, API key, …) |
account_timezone | The account's own timezone — why two platforms can disagree about "today" |
last_synced_at | Last successful sync |
last_error | The most recent failure, if any |
Credentials are never returned. encrypted_credentials is excluded at the query level, not filtered afterwards — there is no request shape that returns it.
Use this to answer "is Bosta connected before I call a shipping route?" in one request, instead of calling the route and interpreting a *_NOT_CONNECTED error.
Error Shape
These domains use Analify's standard codes rather than a platform taxonomy:
| Code | Status | Meaning |
|---|---|---|
BAD_REQUEST | 400 | Malformed JSON body, missing required field, or empty items |
COGS_NOT_FOUND | 404 | No cost data on file for that SKU |
ORG_NOT_FOUND | 404 | The organization does not exist |
{
"success": false,
"error": {
"message": "sku, productName, and costPerUnit (piasters) are required",
"code": "BAD_REQUEST"
}
}Updated about 2 months ago
