Grant a reward
Submit an exact net token allocation from your trusted backend.
Use rewards.issue() to record a reward for activity your application has already qualified. You supply the existing destination wallet and exact token quantity; Slices does not calculate or purchase the reward.
Endpoint#
Request body#
| Field | Required | Description |
|---|---|---|
campaignId | Yes | Approved campaign ID; 1–96 letters, digits, underscores, or hyphens. |
recipient.walletAddress | Yes | Structurally valid, nonzero EVM address. Incorrect mixed-case checksums are rejected. |
amountBaseUnits | Unless a rule applies | Exact positive integer string, within uint256 and campaign limits. This is the recipient's net entitlement. Optional when the campaign has a rule for event.type; required otherwise. |
event.id | Yes | Stable, case-sensitive event reference; 1–128 characters. Do not include personal information. |
event.type | Yes | Case-sensitive reporting label; 1–64 letters, digits, underscores, periods, colons, or hyphens. |
Idempotency-Key is a required header of 8–128 characters using letters, digits, periods, underscores, colons, or hyphens. It is not a field in the HTTP JSON body.
Campaign rules and the amount#
A campaign can carry rules, one per event.type, each naming the reward for one event of that type and, optionally, the most one wallet can earn from that event type (campaign.rules, see Campaign object). When the campaign has a rule for the request's event.type, amountBaseUnits may be left out and the rule's amount is used; a request that does carry amountBaseUnits always uses its own amount, checked against the same limits. A request without an amount for an event type that has no rule is 400 INVALID_REQUEST. A reward that would take a wallet past the rule's per-wallet cap, or past the campaign's maximum grant, is 422 GRANT_LIMIT_EXCEEDED; the SDK's events.track() is this call without an amount.
The request schema is strict. Do not add user, email, metadata, activity, verified, value_usd, chainId, or token overrides. The campaign fixes the asset and network.
TypeScript#
Initialize slices as shown in Install. The SDK moves idempotencyKey into the request header and returns the reward resource without the HTTP envelope.
HTTP example#
Save the request above as issue-request.json, then run it only against the approved environment:
Response#
The first successful acceptance returns 201 Created:
All addresses, IDs, amounts, and timestamps in this example are synthetic. TST on chain 31337 is a local test profile, not an available production asset.
Acceptance means the instruction and its capacity reservation were stored durably. It does not mean your operator approved anything or that a wallet sent anything: the grant is queued for approval once the recipient has verified their wallet.
Retries and duplicate events#
The same key and semantic request return 200 OK, the current reward resource, and Idempotency-Replayed: true. No new reward or capacity movement is created.
The same key with changed content returns 409 IDEMPOTENCY_CONFLICT. Reusing the same campaign/event-type/event-ID tuple under another key returns 409 DUPLICATE_EVENT. Changing the key is not a valid way to retry an uncertain request.
Limits and immutable fields#
Amounts must be canonical strings such as "1000000", not JSON numbers or decimal token values. The per-grant maximum and remaining issuance capacity are checked atomically. See Token amounts.
An accepted reward's recipient, amount, asset, and event are not editable. Cancellation is available only before the operator's approval. An unknown delivery outcome is a reconciliation problem, not permission to issue again.
Related: Idempotency, Reward states, and Errors.