Overview
A redemption converts a user’s $GOLD tokens into a physical-gold denomination (e.g., 1 Tola) that ORO fulfills off-chain in a specific city. The token flow is on-chain — the user transfers tokens into GRAIL’s escrow. Fulfillment (physical pickup) is managed by ORO and progresses through a status lifecycle that the partner can observe but not drive.
Redemptions differ from trades in three important ways:
- Only two signers — GRAIL + user. The partner does not sign.
- Row written at quote time — a redemption row is created at status
quoted as soon as you call POST /v1/redemptions (unlike trades, which are stateless at quote time).
quoted is never surfaced — GET /v1/redemptions/:id returns 404 until the user’s transfer has been confirmed on-chain and the indexer advances status to submitted.
Status lifecycle
- Indexer writes
quoted → submitted (or quoted → failed if the on-chain transfer reverts)
- ORO admin drives
submitted → preparing → ready → collected
- Partner can request cancel only while status is
submitted — once ORO advances to preparing, physical prep has begun and cancellation is a coordination question, not an automated one
Step 1 — Look up available denominations
Response:
Pick the id for Step 2 and note the city — you must pass the same city in the quote body (case-insensitive).
Step 2 — Quote a redemption
Response:
tokens_required is the amount of $GOLD tokens (per troy ounce, 6 decimals) the user must transfer. In the example above: 0.375006 tokens for 1 Tola. This is not the same as total_usd (1827.34) — that’s the USD value of those tokens. Mixing them up is the most common redemption mistake.
Step 3 — Co-sign with the user wallet ONLY
Unlike trades, the partner does not sign. Only the user needs to add their signature.
Step 4 — Submit
Response:
Same passthrough semantics as trade submit — no DB write. The indexer writes the status transition after on-chain confirmation.
Step 5 — Wait, then fetch
Once the indexer has advanced the row:
While the redemption is still at quoted, this endpoint returns 404 redemption_not_found — treat that as “indexer hasn’t confirmed yet”, not as an error.
Tracking fulfillment
The partner cannot drive lifecycle transitions from submitted onward. ORO does that in the admin interface. As a partner, poll GET /v1/redemptions/:id (or use List Redemptions with a status filter) to see when the row advances. Typical transitions:
submitted → preparing when ORO begins physical handling
preparing → ready when gold is at the pickup location
ready → collected after the user collects
Cancelling
Only valid at submitted:
Response:
If the redemption has moved past submitted, you get 400 cancellation_not_allowed. From cancellation_requested, ORO advances to cancelled (final) after confirming no physical prep has started.
The user’s $GOLD has already been transferred to escrow at submitted. Cancellation flags the record — it does NOT automatically return the tokens to the user. Any refund is a coordination question with ORO, not an automated flow.
Listing
Optional filters: grail_user_id, status, city. quoted is always excluded.
Common errors
End-to-end script