Skip to main content

Create order

POST 

/api/v1/orders

Creates a new order.

Request body:

  • ordersType="shop": purchase vouchers/codes.
  • ordersType="dtu": direct top-up (no voucher codes; DTU attributes are returned).
  • ordersType="dtu" + checkOnly=true: validate top-up (quote + provider check) without creating an order.
  • ordersType="steam-gift": Steam Gift purchase (requires quoteToken from POST /steam-gifts/quotes, referenceId, exactly one gift, recipient_region + invite_url/profile_url fields). Feature-gated default-off. Strict same-key/same-payload idempotency (payload conflict → HTTP 409).

referenceId is required for purchases (checkOnly=false) and acts as an idempotency key (unique within your account). If the same referenceId is sent again, the API returns the original result with statusCode=IDEMPOTENCY_REPLAY (even if the request body is different; exception: ordersType="steam-gift" requires the same canonical payload or returns HTTP 409).

If the purchase is accepted for asynchronous processing, the endpoint returns HTTP 202 and status=IN_PROGRESS. Poll GET /orders by orderId or referenceId until a terminal status.

eSIM orders (ordersType="esim")

  • One purchase = exactly one eSIM (quantity is always 1).
  • eSIM orders are always priced and settled in USD.
  • Required scopes: product:read for the catalog, orders:write for this endpoint and orders:read for polling — there is no separate esim scope.
  • The activation payload (result.esim: qrCodeText, iccid, smdpAddress, activationCode, iosInstallUrl, androidInstallUrl, ...) is usually issued asynchronously by the supplier. When it is not ready yet, the endpoint returns HTTP 202 with status=IN_PROGRESS, result=null and a Retry-After: 15 header.
  • Polling guidance: repeat GET /orders?referenceId=... every 10–15 seconds. The upstream finalize sweep runs about once per 60 seconds, so the payload typically appears within 1–2 minutes. Treat ~10 minutes of IN_PROGRESS as a timeout and contact support with the orderId — do NOT re-submit the purchase with a new referenceId.
  • Replaying this POST with the same referenceId is also safe: it returns the refreshed state (statusCode=IDEMPOTENCY_REPLAY) without creating a second order.

Rate-limit / spend-cap responses (important)

There are two distinct refusal paths that historically looked identical to clients. They are now separated by HTTP status and statusCode:

  • HTTP 429 / statusCode=LIMIT_REACHED (8) — a true per-window rate limit (the per-endpoint counter or an upstream shop-service rate-limit). Safe to retry with back-off once the window resets.
  • HTTP 409 / statusCode=API_KEY_LIMIT_EXCEEDED (13), errorCode="api_key_transaction_limit_exceeded" — the API key's static transactionLimit cannot accommodate the quoted order amount. NOT a rate limit; retrying will keep failing. Raise the cap (or reset transactionUsed) through the control plane. The response message includes the attempted amount, the remaining headroom and the configured limit.
  • HTTP 429 / statusCode=LIMIT_REACHED (8) can also be returned for a brief, self-resolving moment when several orders on the same API key are created concurrently and momentarily contend on the key's spend-counter row. The response carries a Retry-After header (typically 1); the order was NOT created and did NOT consume quota. Simply retry after the delay. (Previously this surfaced as HTTP 502 — clients should treat 429 here as retryable rather than a server failure.)

Provider availability (checkOnly=true)

DTU validation (ordersType="dtu" + checkOnly=true) is forwarded synchronously to the underlying provider via shop-service. When the provider does not respond within the tightened read budget, Public API retries the call once with a short jittered backoff. If the retry also times out, the response is:

  • HTTP 504 Gateway Timeout / statusCode=PROVIDER_UNAVAILABLE (14) — the provider failed to answer twice in a row. The response carries a Retry-After: 2 header; safe to retry after that delay. The request did NOT debit funds and did NOT create an order.
  • HTTP 429 / statusCode=LIMIT_REACHED (8) — Public API limits the POST /orders (check) bucket to 120 requests / 60 seconds per account (shared by every API key on it). statusMessage names that bucket and the response carries Retry-After: 60; wait that long before retrying. This budget applies to the pre-check ONLY: creating an order is deliberately not throttled by this bucket, and the same validation runs during creation, so a non-essential pre-check can be skipped rather than retried.

Request

Responses

Successful Response