Quickstart
This quickstart takes a sandbox partner from an API ID and secret to a redeemed eSIM. Every commerce request is signed with HMAC-SHA256. The examples use placeholder identifiers only.
Before you start
- An approved partner account and a sandbox key from the sandbox dashboard. The secret is shown once at creation. Production keys use the production dashboard and the production API.
- Node.js 20 or newer for the signing helper (any runtime with HMAC-SHA256 and SHA-256 works).
- The key scopes you need:
catalog:read,quotes:write,orders:write,orders:read,wallet:read,esims:redeem. - If the key has an IP allowlist, run from an allowed IPv4 or IPv6 address. See IP allowlist.
Keep the secret out of the browser
The API secret must never be placed in a browser bundle, a mobile app, a URL, a log line or a prompt. Sign on your server. If the secret is exposed, revoke the key immediately and contact [email protected].
1. Store the credentials
Create a sandbox key in the dashboard. Store the two values as environment variables:
Code
The API ID is a public identifier with the rw_sandbox_ prefix in sandbox and rw_live_ in production. The secret is a
bearer credential and is never sent directly. The signature proves possession of it.
2. Add the signing helper
Save this as rw-sign.ts. It signs the canonical string described in
Authentication.
Code
The signed path must be exactly the path and query you send, preserving order and encoding.
3. Confirm capabilities
GET /v1/capabilities is public and unsigned. Read it before building a workflow so you can
see which flags are on.
Code
A sandbox response looks like this:
Code
4. List the catalogue
Code
Each offer has an opaque offerId, a display name, destination, validity, data allowance,
prices in all three currencies, and an advisory available flag. Availability is not a stock
reservation. See Catalogue.
5. Create a quote
Code
Quotes are valid for 15 minutes. A quote can carry up to 200 lines and 5,000 units in total.
Check expiresAt, currency, totalMinor and capped before asking for a
payment decision. The quote binds price and FX; it does not move money. See Quotes.
6. Create the order
There are two supported order shapes. Use the quote flow when a human wants to review the total first, or the one-call flow when the agent already has a mandate and an expected ceiling.
Quote-based
Code
One-call
Code
expectedTotalMinor is optional. When present, the API compares it with the freshly computed
total and returns 409 price_changed with the new total instead of charging. This is the safest
one-call shape for an agent.
The Idempotency-Key is required. Use a new key for each logical order and reuse the same key
for retries. Keep reference stable and unique per partner and environment. A successful
response contains purchaseId, status, amountMinor, currency,
orderIds and balanceAfterMinor.
status can be processing, completed or failed. processing means at
least one unit is awaiting upstream fulfilment. Poll GET /v1/orders/{purchaseId} until the
status becomes terminal. See Orders.
A 402 insufficient_funds means no order and no debit was created. Top up the wallet, then
retry only if you intentionally create a new attempt with the same idempotency key and reference.
The target contract adds 402 payment_required with a funding_url; that shape is not
live yet.
7. Redeem
Code
Redeem at most 20 eSIMs per batch. A retry with the same key returns the same fixed batch. The
response contains batchId, format, items (item, code, ICCID, LPA when
requested) and qrSvgs when requested.
LPA and QR are bearer secrets
Deliver redemption output only into the partner's entitled, secure channel. Do not log, cache, email, forward or display LPA or QR data outside that channel. No webhook payload contains LPA or QR data.
Troubleshooting
| Symptom | Likely cause | Action |
|---|---|---|
401 signature_invalid | Wrong secret, canonical string mismatch, or body changed after signing | Recompute over the exact raw body and path used on the wire. |
401 request_expired | Server clock is more than five minutes from the API | Sync time with NTP and recompute the timestamp. |
401 replayed | Nonce reused within ten minutes | Generate a fresh nonce per attempt, including retries. |
403 ip_not_allowed | Egress IP is outside the key allowlist | Use an allowed address or ask the owner to update the allowlist. |
402 insufficient_funds | Wallet available balance is below the quote total | Fund the wallet or reduce the order. Do not retry blindly. The target payment_required shape adds funding_url. |
409 quote_expired | More than 15 minutes passed | Request a new quote. Do not reuse the expired one. |
429 rate_limited | Per-minute limit or daily quota reached | Honour Retry-After, add jitter, and reduce concurrency. |
Next
- Authentication for the exact canonical string and more helpers.
- Orders for statuses and idempotency rules.
- Wallet for funding and balance semantics.
- Agent prompt if an autonomous agent will run this flow.