Physical delivery
Ship real gold and silver coins to an end user's address — minted, insured and tracked to the door.
Delivery turns holdings into metal in someone's hand, so it needs the delivery capability — enable it on the Use cases page. It is a detailed-API flow only; the hosted embed does not expose it.
Because the goods are irreversible once shipped, two guardrails sit in front of every order:
| Guardrail | What it means |
|---|---|
| Approved KYC | The end user must have completed and passed KYC before a quote will price. Otherwise the quote returns 403 KYC_REQUIRED. |
| OTP to the user | The last step is an OTP sent to the user's own registered mobile — not to you. No code, no order. |
Who pays for what
Pick a payment mode per order.
| payment_mode | How it bills |
|---|---|
| redeem | The user's own gold/silver holdings are debited for the coin's metal weight. INR charges (minting + insured delivery + GST) are billed to YOUR partner wallet. |
| buy | No user holdings touched — the full cost (metal + minting + delivery + GST) is billed to YOUR partner wallet at the live rate. |
402 INSUFFICIENT_PARTNER_FUNDS rolls the whole order back.1. Browse the catalog
/partner/delivery/productsdelivery/partner/delivery/products/:skudelivery| Field | Type | Description |
|---|---|---|
| sku | string | Stable identifier for the coin. Pass it to the quote. |
| name | string | Display name, e.g. a 1g 24K gold coin. |
| metalType | string | gold or silver. |
| purity | string | Fineness of the minted coin. |
| productWeight | number | Physical weight of the coin in grams. |
| redeemWeight | number | Grams debited from the user's holdings in redeem mode. |
| pricePerUnitInr | number | Indicative all-in price per coin, ex-GST — metal at the live rate plus minting and insured delivery. For display only. |
| gstPct | number | GST added at checkout, so you can label the indicative price honestly. |
| images | array | Product imagery you can render in your own UI. |
| inStock | boolean | Whether the mint can currently fulfil this SKU. |
curl https://api.oropocket.com/partner/delivery/products \
-H "Authorization: Bearer oro_live_xxx"2. Quote the order
The quote prices the coins, freezes the shipping address and sends the OTP to the user in one call.
/partner/users/:user_code/delivery/quotedelivery| Field | Type | Description | |
|---|---|---|---|
| sku | string | required | The coin to ship, from the catalog. |
| quantity | integer | required | How many coins, 1 to 999. |
| payment_mode | string | required | Either "redeem" or "buy" — see the billing table above. |
| address | object | required | Where to ship. Fields are listed below. |
Inside address:
| Field | Type | Description | |
|---|---|---|---|
| name | string | required | Recipient name, 2 to 50 letters. |
| mobileNumber | string | required | 10-digit contact number for the courier. |
| addressLine | string | required | Full street address, 10 to 100 characters. |
| pincode | string | required | 6-digit Indian PIN code. |
| string | optional | Optional — used for delivery notifications. |
| Field | Type | Description |
|---|---|---|
| partner_quote_id | string | Pass this to the resend and confirm calls. Lives about 5 minutes. |
| product | object | { sku, name, metal_type, quantity } — what is being shipped. |
| payment_mode | string | "redeem" or "buy", echoed back. |
| breakdown | object | { line_items, total_payable_inr } — itemised metal, minting, delivery and GST. |
| billing | object | { source: "partner_wallet", charge_inr } — what will be debited from you. |
| shipping_address | object | The address as we stored it. Check it before confirming; it is frozen into the order. |
| otp | object | { required, sent, channel, sent_to, resend_after_seconds } — sent_to is masked, resend_after_seconds is 45. |
| expires_at | string | ISO timestamp. Confirm before this or request a new quote. |
Quotes fail with 403 KYC_REQUIRED if the user is not verified, and — in redeem mode — 402 INSUFFICIENT_USER_BALANCE if they do not hold enough metal to cover the coin.
curl -X POST https://api.oropocket.com/partner/users/usr_xxx/delivery/quote \
-H "Authorization: Bearer oro_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"sku": "GOLD_COIN_1G",
"quantity": 1,
"payment_mode": "redeem",
"address": {
"name": "Anya Sharma",
"mobileNumber": "9876543210",
"addressLine": "12 Turner Road, Bandra West, Mumbai",
"pincode": "400050"
}
}'3. Resend the OTP (optional)
/partner/users/:user_code/delivery/otp/resenddelivery| Field | Type | Description | |
|---|---|---|---|
| partner_quote_id | string | required | The quote to reissue a code for. Wait out resend_after_seconds (45) first. |
4. Confirm and ship
Confirm verifies the OTP, bills your wallet and places the order with the mint — atomically. Either all three happen or none do.
/partner/users/:user_code/delivery/confirmdelivery| Field | Type | Description | |
|---|---|---|---|
| partner_quote_id | string | required | The quote to execute, confirmed before it expires. |
| otp | string | required | The code the end user received on their registered mobile. |
| Idempotency-Key | header | optional | Strongly recommended. Any string up to 100 characters — retrying with the same key replays the original response instead of shipping twice. |
401 INVALID_OTP; after 5 attempts the quote locks and you need a new one. But if billing fails — 402 INSUFFICIENT_PARTNER_FUNDS or 403 WALLET_NOT_CONFIGURED — everything rolls back and the quote reopens, so you can top up and retry with the same OTP.curl -X POST https://api.oropocket.com/partner/users/usr_xxx/delivery/confirm \
-H "Authorization: Bearer oro_live_xxx" \
-H "Idempotency-Key: 8f14e45f-cea1-4b2c-9d3a-1f2e3d4c5b6a" \
-H "Content-Type: application/json" \
-d '{"partner_quote_id":"pdq_xxx","otp":"1234"}'5. Track the order
/partner/users/:user_code/delivery/ordersdelivery/partner/users/:user_code/delivery/orders/:order_iddelivery| Status | Meaning |
|---|---|
| pending | Order accepted and paid for, waiting to be picked up by the mint. |
| processing | The coin is being minted and packed. |
| dispatched_but_not_delivered | Handed to the courier. Tracking details are now available. |
| delivered_to_client | Signed for by the recipient. Terminal state. |
Tracking fields — awbNo and logisticName — appear once the parcel is dispatched. Status is refreshed from the fulfilment partner every time you read an order, so a poll always returns fresh state.
Testing in sandbox
In sandbox the OTP is always 1234 and no SMS goes out. Orders are simulated end to end — nothing is written to your wallet and nothing reaches the fulfilment partner — so you can exercise the full quote, confirm and tracking loop for free.