A reward cannot be opened empty — the discount is created from the balance, and a zero balance produces an already-expired code — so the reward is opened together with its first entry. The customer must already exist; an unknown customer returns 404. Create them with POST /customers and retry.
Retrying is safe. The opening entry is de-duplicated on shop_transaction_id exactly like any other entry, so a retry after a timeout returns 200 with the existing reward and the entry applied once, never twice. A customer who already holds a reward gets that same 200 rather than a second reward. Note this differs from POST /rewards/{shopRewardId}/transactions, which returns 409 for an already-recorded entry.
The entry's ID in your platform. Unique and stable: it addresses the entry and de-duplicates writes, the same way shop_order_id does for orders. It is what makes a forwarder safe to retry. Avoid / and . — the value travels in a URL path segment.
Positive to credit; negative to reverse credit previously issued. Currency must match the reward's currency. Onward derives the presentment and USD amounts itself — do not send them.
A future timestamp holds the entry as pending issuance until then, after which it becomes spendable. Omit for immediately spendable credit — a past or present value is rejected rather than treated as immediate, so there is exactly one way to express "spendable now". The field is cleared once the entry issues, so a matured entry reports null.
Where the entry came from. name is a stable lowercase slug assigned when your integration is onboarded, and it is the only key Onward reads — it attributes the entry to the platform that sent it, since the API key resolves to a shop rather than to a caller. Every other key belongs to you: Onward stores it verbatim, surfaces it for support, and never interprets it. Keep it a flat object of string values with a small number of keys. There is deliberately no Onward vocabulary for why credit was earned, so your own reason travels here.
{ "name": "inveterate", "reason": "SIGNUP_STORE_CREDIT" }
- https://app.useonward.com/api/v1https://app.useonward.com/api/v1/customers/{shopCustomerId}/rewards
curl -i -X POST \
'https://app.useonward.com/api/v1/customers/{shopCustomerId}/rewards' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: YOUR_API_KEY_HERE' \
-d '{
"shop_transaction_id": "inveterate:1a2b3c",
"amount": {
"cents": 1299,
"currency": "USD"
},
"issues_at": "2019-08-24T14:15:22Z",
"source_details": {
"name": "inveterate",
"reason": "SIGNUP_STORE_CREDIT"
}
}'The customer already held a reward, or this entry was already recorded.
The ID of the discount this reward is tied to. Store it — later calls address the reward by it. Can be null when the discount has not been created yet or has gone missing: the balance is written before the discount is synced, so a platform outage leaves the reward addressable only once the sync retries. Re-read GET /customers/{shopCustomerId} to pick it up. Also null when your shop has no redemption mechanism, since there is nothing to address the reward by.
Credit that is scheduled but not yet spendable, reported separately from the spendable balance.
Credit issued, net of reversals — not a gross "total ever earned". A reversal is stored as a negative entry in the same bucket, so it reduces this figure, and the identity lifetime_issued = balance + lifetime_used always holds. Do not render it as lifetime earnings.
Always zero. Onward does not currently write this bucket — refunds are modelled as reversal ledger entries, not as a lifetime total. Do not build on this field.
How the customer can spend this balance, for example with a discount code. null when there is nothing to redeem: the balance is zero, or the shop has no way to redeem it. More types may be added later, so ignore any type you don't recognize.
{ "shop_reward_id": "1234567890", "balance": { "cents": 1299, "currency": "USD" }, "pending_issuance_balance": { "cents": 1299, "currency": "USD" }, "lifetime_issued": { "cents": 1299, "currency": "USD" }, "lifetime_used": { "cents": 1299, "currency": "USD" }, "lifetime_refunded": { "cents": 1299, "currency": "USD" }, "redemption_details": { "name": "shop", "type": "discount_code", "code": "ONWARD-e77c3802fd6f9d7d6daae8e286a80f11", "url": "https://shop.example.com/discount/ONWARD-e77c3802fd6f9d7d6daae8e286a80f11?ref=onward" }, "balance_updated_at": "2019-08-24T14:15:22Z" }