Skip to main content

Worked incentives samples

The endpoint list below documents every operation’s parameters; this overlay walks an incentive the way a promotions API actually uses it: browse the provider catalog → issue an incentive → list the issued ledger → handle a redeem or void. Success envelopes are { data, meta }, error envelopes { error, meta } — see How to read a worked sample. The standalone promotions engine is state-projected over the tenant cdp_events ledger (the same append-only store the loyalty layer uses), so issuance is migration-free and a read always converges. For the console-side accounting treatment, see the incentives ledger guide. Every response carries meta.request_id. Quote the request id when you report a credit that landed on the wrong ledger — a support trace needs both the row id and the request id.

1. Browse the provider catalog

GET /api/v1/incentives/providers returns every incentive fulfillment provider the engine can call: the internal Orbit reward-code minter and manual-fulfillment recorder, plus external gift-card networks (Tremendous, Tango) that stay descriptors until you connect credentials. Use this to drive the reward-source picker or to decide which provider_id to pin on the issue call. Each row carries configured — true when the engine can dispatch right now, false for an external network awaiting credentials.
200

2. Get a single provider

GET /api/v1/incentives/providers/{id} returns one provider descriptor — the same shape each catalog row carries. Use it to render the detail behind a reward-source picker, or to confirm a provider_id you pinned on issue is still configured. An unknown id returns 404 NOT_FOUND.
200

3. Issue an incentive

POST /api/v1/incentives/issue creates one incentive — a credit, a discount code, a gift card, or a custom payout — and records the outcome on the tenant cdp_events ledger. The body takes reward_type (one of credit, discount_code, gift_card, custom), optional amount / currency, an optional provider_id (when omitted the engine picks a default per reward type), a recipient (contact_id and/or email), and freeform metadata. A write of this kind is gated to owner, admin, or developer roles — the same guard the referral reward-payout surface uses. Send an Idempotency-Key header (or idempotency_key field) to replay the original result on retry; a replay returns 200 with replayed: true rather than minting again.
201

4. List the issued ledger

GET /api/v1/incentives/issued returns every incentive the tenant has issued, newest first, each row folded to its current lifecycle status (issued, redeemed, or voided). Filter by status, source, reward_type, or contact_id; page with limit (1–200, default 50) and offset. Use this to drive a win-back report or the org’s own incentive audit before you call redeem or void.
200

5. Redeem an issued incentive

POST /api/v1/incentives/{id}/redeem marks an issued incentive as redeemed — the recipient applied the code, the gift card was spent, the custom payout was fulfilled. The call appends an incentive.redeemed event under a per-incentive advisory lock, so two concurrent redemptions cannot both consume the same incentive. Only an issued incentive can be redeemed; a terminal one (already redeemed or voided) returns 409. Pass an optional reference (the order or ticket that consumed it) and freeform metadata; the response returns the row in its post-redeem state with status: "redeemed" and redeemed_at set.
200
A second redeem (or a void after redeem) returns 409 CONFLICT with the current lifecycle in error.details so your client can branch instead of retrying:
409

6. Void an issued incentive

POST /api/v1/incentives/{id}/void cancels an issued incentive — the code was returned unused, the recipient opted out, the payout was reversed. The call appends an incentive.voided event under the same per-incentive advisory lock; only an issued incentive can be voided, and a terminal one returns 409. Pass an optional reason; the response returns the row in its voided state with status: "voided" and voided_at set.
200

7. Errors

Errors follow the { error, meta } envelope. Two failures every promotions integrator hits: 403 — role too low for a write.
403
422 — unknown provider or wrong reward semantics. An override that names a provider the catalog cannot resolve, or an amount that picks a negative debit:
422
409 — already terminal. A redeem or void against an incentive that is already redeemed or voided:
409