Core concepts

The vocabulary behind the numbers. Read this before you build anything on the
Analytics API — most integration bugs are not HTTP problems, they're a
misunderstanding of what a field means.


1. Blended profit is the primary number

Blended Profit = Revenue − Ad Spend − Shipping − COGS − Returns/RTO

Every input comes from a different system: revenue from Shopify, ad spend from
Meta/TikTok/Google, shipping and returns from the courier, COGS from you. No
single platform can produce this figure, which is why it's the one Analify
exists to compute.

The efficiency counterpart is nROAS after RTO — never platform-reported
ROAS. Both are on /api/analytics/blended/*.

2. Why platform ROAS misleads on a COD store

A platform counts a conversion when the order is placed. On cash-on-delivery
that is a promise, not money. The order still has to survive the courier.

So purchase_roas from Meta and nROAS after RTO from Analify are answering
different questions, and on an Egyptian store with 20–35% of orders coming back,
the gap is the whole margin. A campaign can look profitable in Ads Manager and
lose money in the bank.

Both numbers are available and both are honest about what they are:

EndpointReturns
/api/core/meta/accounts/{id}/insightsMeta's own reported ROAS, unchanged
/api/analytics/blended/*, /api/analytics/ads/true-profitnROAS after RTO and blended profit

If you're building a "should I scale this?" decision, the second is the one that
answers it.

3. The COD lifecycle, and the cash gap

confirmed → courier → attempted → delivered ──→ settlement
                          └────→ RTO (returned to origin)

Two consequences that shape every shipping number:

Cash lands 7–14 days after delivery. Revenue and cash are not the same week.
Sales can be up while the bank balance is flat — that's the settlement lag, not
an error. /api/analytics/integration/{id}/rich/shipping-cod-settlement is where
that gap is quantified.

One RTO wipes the profit of 3–5 delivered orders. You paid to acquire, paid
to ship out, and paid to ship back, for zero revenue. This is why return rate
sits next to profit rather than in a separate report.

4. RTO is one number, not two

Bosta's raw analytics response carries both unsuccessfulOrders and returns,
and returns is contained inside unsuccessfulOrders. Adding them
double-counts your return rate.

Analify resolves this in exactly one place, so every surface agrees. If you read
the raw endpoint yourself (/api/core/bosta/analytics/success-rate), do not sum
the two — and prefer the computed shipping views unless you specifically want
Bosta's untouched body.

5. A profit number is only as good as your COGS coverage

Analify can derive revenue, ad spend, shipping and returns. It cannot derive what
your product cost you. Without COGS, "profit" is revenue minus the costs we
happen to know.

So coverage is a first-class metric, not a settings detail:

curl -s "https://analify.scalyax.ai/api/core/cogs/coverage?organizationId=$ORG" \
  -H "Authorization: Bearer $ANALIFY_TOKEN"

Low coverage should visibly weaken any conclusion you draw downstream — that's
the same rule the product follows internally: when confidence is low, show the
number and soften the verdict rather than asserting a decision.

6. Two platforms can disagree about "today"

Ad platforms report in the ad account's local timezone; Shopify reports in
the store's. An org with a Cairo store and a US-registered ad account has two
different "todays", and a same-day comparison across them will not reconcile.

  • Send calendar dates (from/to, YYYY-MM-DD, both inclusive). The server
    handles conversion — don't pre-offset them.
  • Each integration's own zone is on account_timezone from
    /api/core/integrations.
  • For a single trustworthy day, prefer a range that is fully in the past.

7. Freshness: nothing is precomputed

Metrics are fetched live from the platforms on every request. There are no
projection tables and no stale fallbacks: if an upstream fetch fails you get an
error or an explicit unavailable / reconnectRequired field — never
yesterday's number dressed as today's.

The one exception is a bounded read-through cache on the highest-volume
aggregator routes:

  • 60s when the requested range includes today
  • 300s when the range is fully in the past
  • entries always expire and re-fetch live; a failure never serves a cached value

Practical effect: polling those routes faster than once a minute returns the
identical body. Cached routes are marked (cached) in the reference.

8. Money units — the rule that costs the most to get wrong

SurfaceMoney arrives as
Analytics API (all of it)integer piasters (EGP × 100)
Core API — Analify-native COGSinteger piasters
Core API — Shopify ShopifyQL FROM salesEGP major units
Core API — Google Adsmicros (÷ 1,000,000)
Core API — Metaaccount-currency decimal strings
Core API — BostaEGP major units

On the Analytics API a Minor suffix (spendMinor, netSalesMinor) always
means piasters, and unsuffixed money fields (spend, revenue, cogs, aov)
are piasters too unless an endpoint says otherwise. Ratios and percentages are
never scaled.

Never round piasters server-side to "make them EGP", and never send floats back.


Next

  • Recipes — these concepts as working scripts
  • README — auth, conventions, caching, rate limits in full
  • date-limits.md — how far back each platform will go

Did this page help you?