Send purchase-created
Reports that a referred user paid for something. Requires amount,
currency, invoiceId and subscription on top of the common subject
block. subscription.interval is required.
amount is an integer in the minor unit of currency, so 49.00 EUR is
4900. A zero amount is permitted here, because a trial conversion or
a fully discounted first invoice is a real purchase event.
Headers
An opaque key identifying this submission attempt, so that a retry can be recognised as the same event.
A repeat of the same request, for the same tenant and event type, within
72 hours returns the original 202 — the same eventId, receivedAt,
and warnings — and does not create a second event. A repeat that uses
the same key with a different body is refused with 409 and
IDEMPOTENCY_CONFLICT. After 72 hours the same key is a new submission.
The same key may be sent in the idempotencyKey body field instead. If
both are sent they must be equal after trim, otherwise the request is
refused with IDEMPOTENCY_KEY_CONFLICT — there is no precedence rule,
because guessing which one the caller meant is worse than saying so.
Leading and trailing whitespace is trimmed on both forms before the key is stored or compared. The 1–255 character limit applies to that trimmed value; a key that is empty after trim is refused.
1 - 255Body
The unique Cello code that attributes this event to a referral.
1 - 255"ucc_7f3d9a"
When the event happened in your system, as an RFC 3339 date-time with an
explicit UTC offset: either Z or ±HH:MM.
The offset is mandatory and the T separator is mandatory. A local time
with no offset is ambiguous by exactly the amount that makes referral
attribution wrong, and 2022-10-05 14:14:34 is refused even though v1
tolerated it.
A syntactically valid timestamp is never refused for being implausible,
however far it is from our clock. If it looks wrong we accept the event
and tell you so in warnings, because dropping a real purchase over a
misconfigured clock is the worse failure.
"2026-08-05T16:14:34+02:00"
"2026-08-05T14:14:34Z"
The referred user this event is about. Only id is required; the rest
improves what Cello can show in the portal.
An uppercase ISO 4217 alphabetic code, validated against the code list
rather than against a three-letter pattern, so EUR is accepted and
eur and XYZ are both refused with a message saying which rule they
broke.
The same code selects the minor-unit exponent that interprets amount
in documentation (JPY is ¥1580, not 158000). The exponent is not used
to rescale v1 payload.price; v1 is already in minor units.
"JPY"
"USD"
"EUR"
The payment being reported or reversed. Any stable identifier from your billing system is acceptable, as long as the refund for a purchase carries the same one the purchase did. Required on both money endpoints, because without it a refund cannot be matched to what it reverses.
1 - 255"inv_456"
Required. interval is required.
Zero is permitted: a trial conversion or a fully discounted first invoice is a real purchase worth attributing.
0 <= x <= 90071992547409914900
The body form of the Idempotency-Key header. See that header for the
full description. Leading and trailing whitespace is trimmed; the 1–255
character limit applies after trim.
1 - 255"purchase-inv_456"
Response
The event was validated and durably written to the event stream.
This says the event was accepted, not that attribution or reward accrual has run. Those happen asynchronously afterwards.
A UUIDv7 assigned by Cello. It is the identifier of the stored event
(id on the stream record). Quote it in support conversations. Never
accepted from a request body.
"019205ff-8e00-7cc0-9f2e-2b4c1d5a6e70"
When Cello received the event, from our clock. Compare it with your
occurredAt to see the skew we saw. Never accepted from a request
body.
"2026-08-05T14:14:35.123Z"
Non-fatal observations about an accepted event. Absent entirely when there is nothing to report, rather than present and empty.
A warning never changes the status code and never changes how the event is processed: an event carrying warnings is stored, attributed and rewarded exactly like one without.
New codes may appear at any time without a version bump, so treat an unrecognised code as informational rather than failing on it.
1