Partner Rewards
For endpoint schemas, request/response examples, and error definitions, see the Rewards API reference.
The Partner Rewards API lets trusted backend integrations issue rewards to customers identified by phone number. Rewards are deposited into the customer's wallet when possible, or held as a pre-issued reward until the customer creates or activates a wallet.
Use this API when a partner system — such as a customer-support platform — needs to compensate or reward a customer on behalf of a merchant.
When to use
- Issue a one-time reward to a customer after a support interaction, promotion, or goodwill gesture
- Look up previously issued rewards for a phone number
- Update or cancel a pre-issued reward before the customer claims it
All requests require your Client ID and Client Secret. See Merchant API Integration for authentication details.
Unified reward resource
Every issue, lookup, list, and update response uses the same JSON:API resource:
typeis alwaysReward(immediate credits and pre-issued rewards share one shape)kindisimmediateorpreIssued(informational only — do not branch behavior on this field alone)statusis always set:Creditedfor immediate wallet credits;Created,Claimed,Expired, orCancelledfor pre-issued rewardscanUpdate/canCancelindicate whether PATCH or DELETE is allowed for that rewardclaimExpiresAtis the claim deadline for pre-issued rewards (ISO 8601 datetime);nullfor immediate wallet credits
Issue flow
When you call POST /api/v1/rewards, the API resolves the recipient's wallet state and chooses one of two outcomes:
Immediate credit (kind: immediate)
When the recipient has exactly one active wallet, the reward is credited immediately. The response has kind: immediate, status: Credited, walletId set, and canUpdate / canCancel are false.
Pre-issued reward (kind: preIssued)
When no active wallet exists and no blocking wallet is found, a pre-issued reward is created. The response has kind: preIssued, status: Created, walletId: null, and canUpdate / canCancel are true. The customer claims the reward when they sign up and create a wallet. Pre-issued rewards include claimExpiresAt — the deadline by which the customer must claim the reward (typically 90 days from creation). If unclaimed by that date, the reward moves to Expired.
Idempotency
Every issue request must include an idempotencyKey (1–255 characters). You do not send a hash — the API computes one server-side from the request body and stores it with the key.
Idempotency is scoped to (idempotencyKey, request body fingerprint). The fingerprint is a SHA-256 hash of the normalized phone number plus:
amountmessage(optional)reasonCode(optional)costCenter(optional)notificationEmail(optional)
Empty or whitespace-only optional strings are treated as absent.
- Same key, identical body — the API returns the original result without creating a duplicate reward. Safe to retry on network failures by resending the exact same request.
- Same key, different body — any change to the fields above (e.g. different
amountorreasonCode) returns409 IdempotencyConflict, even if the phone number is unchanged.
Generate a unique key per logical reward action (e.g. reward-{caseId}-{timestamp}). Use a new key when you intentionally change the reward details.
Example: issue to an active wallet
POST /api/v1/rewards
{
"data": {
"type": "IssueReward",
"attributes": {
"phoneNumber": "+15551234567",
"amount": 500,
"idempotencyKey": "reward-case-98765",
"reasonCode": "CX-COMPENSATION",
"message": "Thank you for your patience!"
}
}
}
Response (kind: immediate):
{
"data": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"type": "Reward",
"attributes": {
"kind": "immediate",
"amount": 500,
"walletId": "9f755746-13cb-4d0b-81f2-3b4f1b44f6d8",
"status": "Credited",
"canUpdate": false,
"canCancel": false,
"claimExpiresAt": null,
"message": "Thank you for your patience!",
"reasonCode": "CX-COMPENSATION",
"costCenter": null,
"notificationEmail": null
}
}
}
Example: issue as pre-issued reward
Same request shape. When no active wallet exists:
{
"data": {
"id": "4cf95060-dd01-42ac-9020-8ca42004920d",
"type": "Reward",
"attributes": {
"kind": "preIssued",
"amount": 500,
"walletId": null,
"status": "Created",
"canUpdate": true,
"canCancel": true,
"claimExpiresAt": "2026-09-15T12:00:00.000Z",
"message": "Thank you for your patience!",
"reasonCode": "CX-COMPENSATION",
"costCenter": null,
"notificationEmail": "agent@partner.example"
}
}
}
Lookup, update, and cancel
List requests use the platform JSON:API query shape: filter[<field>] for filters and page[offset] / page[limit] for pagination (same as users, payment intents, and external transactions).
| Action | Endpoint | Notes |
|---|---|---|
| List | GET /api/v1/rewards?filter[phoneNumber]=...&page[offset]=0&page[limit]=10 | filter[phoneNumber] is required; optional filter[status] for pre-issued rewards |
| Get by ID | GET /api/v1/rewards/{id} | Returns { data: null } when not found |
| Update | PATCH /api/v1/rewards/{id} | Only when canUpdate is true; at least one field required |
| Cancel | DELETE /api/v1/rewards/{id} | Only when canCancel is true; returns 204 |
Example list request:
GET /api/v1/rewards?filter[phoneNumber]=%2B15551234567&filter[status]=Created&page[offset]=0&page[limit]=10
Reward statuses: Credited (immediate), Created, Claimed, Expired, Cancelled.
Error handling
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
InvalidRequest | 400 | Invalid phone, amount, email, or missing filter[phoneNumber] on list | Fix the request payload or query |
WalletNotFound | 400 | Wallet not found for recipient | Verify the phone number |
MultipleActiveWallets | 400 | More than one active wallet matches | Escalate — data integrity issue |
WalletNotIssuable | 409 | Wallet exists but cannot receive rewards (e.g. closing) | Wait for wallet state to resolve or use a different channel |
PreIssuedConflict | 409 | Unclaimed pre-issued reward already exists | Update or cancel the existing pre-issued reward |
IdempotencyConflict | 409 | Same idempotency key, different request body | Use a new key, or resend the exact original body (phone, amount, and metadata) |
NotFound | 404 | Reward not found | Verify the ID |
InvalidState | 409 | Reward not updatable or cancellable in its current state | Check canUpdate / canCancel before calling PATCH or DELETE |