Get started

Errors & rate limits

Predictable error shapes, idempotent writes and documented limits.

Error shape

Every failure returns the same envelope. request_id identifies the exact call — include it when you contact support. details is present on validation failures.

{
  "success": false,
  "request_id": "req_9f2c…",
  "mode": "live",
  "error": {
    "code": "QUOTE_EXPIRED",
    "message": "This quote expired. Request a new one.",
    "details": { }
  }
}

Error codes

FieldTypeDescription
INVALID_AMOUNT400optionalAmount missing or non-positive.
INVALID_RECEIVER400optionalMobile number malformed.
QUOTE_EXPIRED400optionalQuote confirmed after its 10-minute TTL.
INSUFFICIENT_BALANCE400optionalYou don't hold enough of the asset.
SANDBOX_ONLY400optionalEndpoint is sandbox-only (e.g. /sandbox/reset).
INVALID_ASSET400optionalAsset was not "gold" or "silver".
INVALID_INTERVAL400optionalPrice-history interval was not "day" or "hour".
INVALID_FROM400optionalPrice-history from date was not YYYY-MM-DD.
INVALID_TO400optionalPrice-history to date was not YYYY-MM-DD.
INVALID_RANGE400optionalPrice-history from is later than to.
INVALID_TOKEN401optionalMissing, malformed or unrecognised bearer token.
INVALID_OTP401optionalWrong OTP. 5 attempts, then the quote locks.
INSUFFICIENT_PARTNER_FUNDS402optionalYour wallet float / credit line can't cover this buy.
INSUFFICIENT_USER_BALANCE402optionalThe end user doesn't hold enough to sell or redeem.
API_NOT_ALLOWED403optionalYour profile isn't enabled for this capability.
LIVE_NOT_ENABLED403optionalLive mode not activated yet. Sandbox keeps working.
SANDBOX_DISABLED403optionalSandbox access disabled for your partner.
PARTNER_DISABLED403optionalMaster kill-switch; both modes disabled.
KYC_REQUIRED403optionalThe user must complete KYC first.
WALLET_NOT_CONFIGURED403optionalPartner wallet not set up — a separate onboarding step.
FEATURE_DISABLED403optionale.g. instant KYC is off for your profile.
QUOTE_NOT_FOUND404optionalquote_id was never issued, or isn't yours.
USER_CODE_NOT_FOUND404optionaluser_code isn't recognised for your partner.
USER_CODE_CONSUMED409optionalSingle-use embed code already exchanged.
QUOTE_ALREADY_CONSUMED409optionalQuote already used. Replay with the original Idempotency-Key.
DAILY_LIMIT_EXCEEDED429optionalThe send breaches your daily INR limit.
RATE_LIMITED429optionalToo many requests in the window — see below.
SANDBOX_DAILY_LIMIT429optionalSandbox daily budget spent. Live keys are unaffected.
BUY_PROCESSING_FAILED500optionalFulfilment failed after debit; your wallet is auto-refunded and the order voided.
PRICES_UNAVAILABLE503optionalUpstream price feed is down.

Idempotency

Send an Idempotency-Key header on mutating endpoints (buy, sell, send, gift and delivery confirms) to make retries safe. Any string up to 100 characters — a UUID is ideal.

Keys are scoped to your partner profile and endpoint and stay valid for at least 24 hours. Reusing one returns the original response with no new side effects, flagged by an idempotent_replay field in the envelope and an Idempotent-Replayed: true response header.

curl -X POST https://api.oropocket.com/partner/buy/confirm \
  -H "Authorization: Bearer oro_live_xxx" \
  -H "Idempotency-Key: 8f14e45f-cea1-4b2c-9d3a-1f2e3d4c5b6a" \
  -d '{"quote_id":"tqt_xxx"}'
If a confirm times out, retry it with the same key. Without one you risk a duplicate — or a QUOTE_ALREADY_CONSUMED you can't safely interpret.

Rate limits

FieldTypeDescription
POST /auth/request-otp5 / 15 minoptionalPer (IP, mobile).
POST /auth/verify-otp10 / 15 minoptionalPer (IP, mobile).
All other endpoints120 / minoptionalPer IP.
Sandbox, all endpoints1000 / dayoptionalPer partner, cumulative. Resets 00:00 IST. Live is not capped this way.

The per-minute limits are per IP. The sandbox daily cap is per partner and counts every authenticated request you make in sandbox, whatever the endpoint and whether it succeeds or returns a validation error — so a tight polling loop spends it just as fast as real testing does. Rotating your sandbox token does not reset it.

Sandbox responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (epoch seconds, the next 00:00 IST), so you can see where you stand without waiting to be blocked. Past the cap you get 429 SANDBOX_DAILY_LIMIT with a Retry-After. Your live token keeps working throughout.

If a genuine integration needs more than 1000 sandbox calls a day, ask — the ceiling is configurable per partner.

Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset — back off when remaining hits zero rather than retrying blindly.

Every request you make is logged in the dashboard with its status and error code — debug a failed call with request logs.