Skip to main content

Quick start

1. Get an API key

Create a key in the Dashboard. While creating it you choose two things that will shape every later call:

  • Scopes — which sections the key may touch (product:read, orders:write, …).
  • IP allowlist — optional. If set, requests from other addresses get HTTP 403.

Copy the token immediately: it is stored hashed and cannot be shown again.

2. Verify the key

GET /whoami is the cheapest possible authenticated call. It answers "is my key working, what can it do, and which IP do you see me from?".

curl https://approute.io/api/v1/whoami \
-H "X-API-Key: $APPROUTE_API_KEY"

A healthy response:

{
"status": "SUCCESS",
"statusCode": 0,
"statusMessage": "OK",
"traceId": "0f2c...",
"data": {
"clientIp": "203.0.113.42",
"scopes": ["product:read", "orders:write"]
},
"errors": null
}

:::tip Allowlist debugging data.clientIp is the address we resolve for you, after proxies. If your key has an IP allowlist and you are getting 403, compare it against what you put in the allowlist — a dual-stack host that quietly egresses over IPv6 is the usual culprit. :::

3. Read the catalog

curl "https://approute.io/api/v1/services?limit=5" \
-H "X-API-Key: $APPROUTE_API_KEY"

Catalog reads need product:read. The envelope status on any list endpoint is always SUCCESS — it means "request served", nothing about the items inside.

4. Dry-run an order

Before spending money, send the same purchase with checkOnly: true. You get back price and availability without creating an order.

curl -X POST https://approute.io/api/v1/orders \
-H "X-API-Key: $APPROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ordersType": "dtu",
"checkOnly": true,
"orders": [{"denominationId": "...", "amount": 1}]
}'

:::warning Content-Type is enforced POST, PUT and PATCH without Content-Type: application/json are rejected with HTTP 415 before any validation runs. :::

5. Place the real order

Flip checkOnly to false and add a referenceId — your own unique string for this purchase. It is what makes the call safe to retry.

curl -X POST https://approute.io/api/v1/orders \
-H "X-API-Key: $APPROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ordersType": "dtu",
"checkOnly": false,
"referenceId": "order-2026-07-27-0001",
"orders": [{"denominationId": "...", "amount": 1}]
}'

Then branch on data.status, not on the HTTP code:

data.statusWhat it meansWhat to do
SUCCESSDeliveredDone
IN_PROGRESSStill working (HTTP 202 + Retry-After)Poll GET /orders
CANCELLEDFailedRead statusMessage / errors

Re-sending the same referenceId returns the original result with statusCode: 2 — no second purchase. See Idempotency.

6. Poll and retrieve the result

Use the original API key with orders:read and the same referenceId. For a shop order, unhide=true returns full voucher codes when they become available; DTU orders return their status and top-up result without voucher codes.

curl -i --get https://approute.io/api/v1/orders \
-H "X-API-Key: $APPROUTE_API_KEY" \
--data-urlencode 'referenceId=order-2026-07-27-0001' \
--data-urlencode 'unhide=true'

Inspect data.page.items[].status. The list's outer status: SUCCESS means the read succeeded; an item may still be IN_PROGRESS.

  • You can keep unhide=true throughout polling. The default limit is 60 requests / 60 seconds per order for IN_PROGRESS and PARTIALLY_COMPLETED, shared by workers reading the same order.
  • SUCCESS and CANCELLED use a separate 1 request / 60 seconds per order allowance. Earlier pending polls do not spend it. Repeated completed reads, including cached codes, consume this same allowance.
  • Save the result and stop polling on SUCCESS, CANCELLED or PARTIALLY_COMPLETED. The latter is terminal partial delivery and may already contain codes despite using the pending budget.
  • On 429, wait for Retry-After before retrying the same order. A lost completed response may require waiting for the next window.

For status-only reads, omit unhide or set it to false; codes are masked and the filtered account-wide budget applies. See Rate limits for effective settings, checkout scopes and the additional admission limit.

Where to go next