- Server to server only. Call it from your backend, never from a browser. The key must stay secret.
- Informational, never a payout surface. Credit your users only from the payout endpoint of your partner wire and its checkpoint protocol. These figures come from the same ledger, aggregated differently, without the anti-double-credit protocol.
- Additive versioning. Fields may be added; existing fields are never removed or repurposed. Ignore keys you do not recognise.
Authentication
- Paths are served at the root, without
/api. - Each key is issued per partner and bound to an explicit list of store ids. A valid key used on a store outside that list gets the same
404as a store that does not exist. - This is a read credential. It is never the secret you use to sign order events; the two are deliberately separate so a leaked read key cannot forge orders.
- Every response carries
Cache-Control: no-store. Do not cache responses.
Endpoints
Store totals
clicks.totalcounts genuine redirect clicks across all of the store’s campaigns, including ended ones.conversions.totalcounts distinct referred orders that converted. A later refund does not remove an order from this count.reversals.affectedConversionscounts orders currently refunded, partially refunded or charged back.heldis a point-in-time escrow balance and can go up or down.releasedLifetimeandreversedLifetimeonly ever increase.asOfis the single database instant every figure was true at.
Campaigns
Returns every campaign of the store in every state (DRAFT, ACTIVE, END, COMPLETE), newest first, capped at 100.
campaignIdis opaque. It is not a UUID; never parse it.titleis display-only and may benull.scope.externalProductIdis present only onproductcampaigns; the key is absent onstore-wideones.rewardModeisLAST_CLICK_ONLY(the last-click creator receives the whole order commission) orFIRST_LAST_80_20(if another eligible creator first introduced the shopper, they receive 20% of it and the last-click creator the remaining 80%). It is campaign metadata only: noearningsfigure changes with it.returningOrderscounts orders tracked but not paid because the buyer had already bought in this campaign. It never overlapsconversions.- Reconciliation guarantee. For each of
held,releasedLifetimeandreversedLifetime, the sum overcampaigns[].earningsplusearnings.unallocatedequals the store total from the same snapshot, even whentruncatedistrue.unallocatedholds money that cannot be placed on a returned row (normally zero). - Ended campaigns keep updating: a late refund still moves their reversals.
Campaign creators
Query:limit (default 50, 1 to 100) and cursor.
externalUserId. This route carries no money.
Creator clicks
Query:limit (default 100, 1 to 500) and cursor.
clickId is your reconciliation key. occurredAt is when the click happened. outcome describes the click’s own order:
returningCustomer is true when any order through this click was tracked but not paid because the buyer was returning. Outcomes change over time (no-conversion to converted to refunded). Re-read by clickId and replace the older value.
Time series
Query parameters:
Each bucket reports
clicks, conversions, commissionAccrued, released and reversed, plus a totals object that always equals the sum of the buckets. startsAt is an absolute UTC instant; bucket boundaries follow the chosen zone, so a daylight-saving day has 23 or 25 hourly buckets. Empty buckets are included. At most 1,000 buckets per request.
The series reports flows only. There is no held (a balance has no meaning per bucket) and no reversal count. Conversions land on your event’s occurrence time if you send one, otherwise on ingestion time, which can lag.
Pagination
The creator and click routes use keyset cursors:- Call without
cursor. - While
nextCursoris notnull, call again withcursor=<nextCursor>exactly as received, not re-encoded. - Stop when
nextCursorisnull.
asOf is per page, not a frozen snapshot of the walk. New clicks arrive at the front of the clicks feed, so restart from page one for a fresh reconciliation. Cursors are traversal tokens, not bookmarks; do not store or construct them.
Errors
Gotchas
- Money is a decimal string. Parse with a decimal type, not a float.
- Counts never shrink; balances can. Use
releasedLifetimedeltas, notheld, to reason about what was paid. - Late refunds. A refund arriving after a commission was already released writes no reversal entry today, so
reversedLifetimecan understate total reversals. - Two requests are two snapshots. The store totals and the campaigns list agree when read at the same instant; separate calls can differ by activity in between.
- This is referred activity, not your order book. Orders that were never referred are not counted.
- Personal data.
externalUserIdis your own pseudonymous id. It is returned only to the partner that supplied it. Use it only for referral attribution and reconciliation, and do not cache it beyond that. Requests are access-logged with credentials redacted.