Recipes
Working scripts for the things people actually build. Each one names the decision
it supports, not just the endpoint it calls.
Assumes $ANALIFY_TOKEN and $ORG are set — see Quickstart.
Money on the Analytics API is integer piasters; on the Core API it's the
platform's unit (Core concepts §8).
1. Daily profit brief
Decision: did yesterday make money, and is the trend moving?
One call, bundled comparison, no second request:
YESTERDAY=$(date -u -v-1d +%F 2>/dev/null || date -u -d yesterday +%F)
curl -s -G "https://analify.scalyax.ai/api/analytics/blended/business-overview" \
-H "Authorization: Bearer $ANALIFY_TOKEN" \
--data-urlencode "organizationId=$ORG" \
--data-urlencode "from=$YESTERDAY" \
--data-urlencode "to=$YESTERDAY" \
--data-urlencode "compare=true" \
| python3 -c '
import sys, json
d = json.load(sys.stdin)["data"]
cur, prev = d.get("current", d), d.get("previous") or {}
egp = lambda v: f"EGP {v/100:,.2f}" if isinstance(v, (int, float)) else "n/a"
for k in ("netProfitMinor", "revenueMinor", "adSpendMinor"):
if k in cur:
delta = ""
if k in prev and prev[k]:
delta = f" ({(cur[k]-prev[k])/prev[k]*100:+.1f}% vs prior)"
print(f"{k:20s} {egp(cur[k])}{delta}")
'Run it before touching anything else — a profit brief that arrives in your inbox
beats a dashboard you have to remember to open. Field names vary by endpoint;
inspect the payload once and pin the ones you need.
Yesterday is a fully-past range, so it's the most stable window to report
on and it caches for 300s.
2. Reconcile Bosta settlements against your own numbers
Decision: how much delivered revenue is still sitting with the courier?
Two different lenses, deliberately:
# Analify's computed settlement view — the cash gap, quantified
curl -s -G "https://analify.scalyax.ai/api/analytics/integration/$BOSTA_INTEGRATION_ID/rich/shipping-cod-settlement" \
-H "Authorization: Bearer $ANALIFY_TOKEN" \
--data-urlencode "organizationId=$ORG" \
--data-urlencode "from=2026-08-01" \
--data-urlencode "to=2026-08-16"
# Bosta's own cash cycles, untouched — the source of truth for shipping fees
curl -s -G "https://analify.scalyax.ai/api/core/bosta/analytics/cash-cycles" \
-H "Authorization: Bearer $ANALIFY_TOKEN" \
--data-urlencode "organizationId=$ORG" \
--data-urlencode "start_date=2026-08-01" \
--data-urlencode "end_date=2026-08-16"Note the parameter names differ — from/to on the Analytics API,
start_date/end_date on the Bosta pass-through, because that's what Bosta
itself accepts. Core API never renames a platform's params.
Cash cycles is where per-delivery shipping fees actually live; the delivery
search endpoint doesn't expose them, because Bosta bills them in batches. If you
need true contribution margin, this is the number to use.
Get $BOSTA_INTEGRATION_ID from /api/core/integrations.
3. Nightly COGS sync from your ERP
Decision: keep profit trustworthy without anyone remembering to update a
spreadsheet.
costPerUnit is piasters — an integer. Sending 125.00 instead of 12500
understates cost 100× and silently inflates every profit number downstream.
# your ERP export → [{sku, productName, costPerUnit}] in piasters
python3 - <<'PY' > /tmp/cogs.json
import csv, json
items = []
with open("/path/to/erp-export.csv") as f:
for row in csv.DictReader(f):
items.append({
"sku": row["sku"],
"productName": row["name"],
"costPerUnit": int(round(float(row["unit_cost_egp"]) * 100)), # → piasters
})
print(json.dumps({"items": items}))
PY
curl -s -X POST "https://analify.scalyax.ai/api/core/cogs/bulk?organizationId=$ORG" \
-H "Authorization: Bearer $ANALIFY_WRITE_TOKEN" \
-H "Content-Type: application/json" \
--data @/tmp/cogs.json
# then confirm the catalogue is actually covered
curl -s "https://analify.scalyax.ai/api/core/cogs/coverage?organizationId=$ORG" \
-H "Authorization: Bearer $ANALIFY_WRITE_TOKEN"This needs a write-scoped token. items must be non-empty — an empty array
is rejected rather than treated as a no-op, because an empty bulk write is
almost always a bug in the caller.
costPerUnit: 0 is valid and is stored — a free sample is a real cost of zero,
not a missing value.
4. Pause what's losing money after returns
Decision: kill the campaigns that only look profitable.
Read with the profit-true lens, then act through the platform:
# 1. Analify's true profit per campaign — after RTO, not platform ROAS
curl -s -G "https://analify.scalyax.ai/api/analytics/ads/true-profit" \
-H "Authorization: Bearer $ANALIFY_TOKEN" \
--data-urlencode "organizationId=$ORG" \
--data-urlencode "from=2026-08-01" \
--data-urlencode "to=2026-08-16" \
> /tmp/true-profit.json
# 2. inspect, decide, and only then mutate — Meta's own field, forwarded as-is
curl -s -X POST "https://analify.scalyax.ai/api/core/meta/campaigns/$CAMPAIGN_ID?organizationId=$ORG" \
-H "Authorization: Bearer $ANALIFY_WRITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "PAUSED"}'The shape of this recipe is the point: decide on the Analytics API, act on the
Core API. A campaign at 3.5× platform ROAS can be underwater once returns are
counted, and Ads Manager will never tell you that.
Step 2 changes a live ad account immediately and has no dry-run. Keep a human in
the loop, or gate it on a threshold you've watched for a couple of weeks.
5. Export orders for finance
Decision: hand accounting the real order list without a CSV download.
Shopify pagination is a cursor, not page numbers:
CURSOR=""
while :; do
RESP=$(curl -s -G "https://analify.scalyax.ai/api/core/shopify/orders" \
-H "Authorization: Bearer $ANALIFY_TOKEN" \
--data-urlencode "organizationId=$ORG" \
--data-urlencode "first=100" \
--data-urlencode "query=created_at:>=2026-08-01" \
${CURSOR:+--data-urlencode "after=$CURSOR"})
echo "$RESP" >> /tmp/orders.ndjson
CURSOR=$(echo "$RESP" | python3 -c '
import sys, json
d = json.load(sys.stdin).get("data", {})
# Core API returns Shopify\'s own body — walk to the connection\'s pageInfo
def find(o):
if isinstance(o, dict):
if "pageInfo" in o: return o["pageInfo"]
for v in o.values():
r = find(v)
if r: return r
return None
pi = find(d) or {}
print(pi.get("endCursor","") if pi.get("hasNextPage") else "")
')
[ -z "$CURSOR" ] && break
doneMoney here is Shopify's unit, not piasters. If you're reconciling against an
Analytics API figure, scale one side before comparing.
For aggregate revenue rather than a row dump, POST /api/core/shopify/analytics/shopifyql
is one call — but note FROM sales returns EGP major units.
Patterns worth copying
- Decide on Analytics, act on Core. One layer tells you the truth about
money; the other changes the thing. Don't make decisions from raw platform
numbers on a COD store. - Ask for the comparison, don't compute it.
compare=truereturns
{ current, previous }in one response. Two calls for two periods is a bug. - Prefer fully-past ranges for anything you report on — stable, and cached
for 300s instead of 60s. - Check COGS coverage before quoting profit. A precise-looking number over a
half-priced catalogue is worse than no number. - Read before you write. Every Core API mutation hits a live account with no
dry-run.
Updated about 2 months ago
