API Reference

Conventions

Base URL, request/response headers, pagination, and date formats used across the entire API

Every endpoint in the Octopus Cards API follows the same conventions for authentication, headers, pagination, and date formats. Read this once and the rest of the reference reads itself.

Base URL

https://api.octopuscards.io

Production uses a separate host and credentials - see Environments.

Authentication

All API requests (except login) require a Bearer token in the Authorization header:

curl https://api.octopuscards.io/api/v1/products \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

See the Authentication reference for the full login, refresh, and logout flow.

Request Headers

Every request can include these headers:

HeaderRequiredDescription
AuthorizationYes (except /auth/login)Bearer <access_token> - your JWT from the login endpoint
Content-TypeYes (POST/PUT/PATCH)Must be application/json
X-Request-IdNoYour own trace ID (KSUID format). If omitted, the server generates one and returns it in the response. Use this to correlate requests across your systems.

Response Headers

Every response includes:

HeaderDescription
X-Request-IdThe request trace ID - either echoed from your request or server-generated

List endpoints also include pagination headers:

HeaderDescriptionExample
X-PageCurrent page number (1-based)1
X-Per-PageItems per page50
X-Total-CountTotal items across all pages250
X-Total-PagesTotal number of pages5
X-Page-SizeItems in the current page (may be less than X-Per-Page on the last page)50
X-Has-MoreWhether more pages existtrue

Pagination

List endpoints accept these query parameters:

ParameterDefaultRangeDescription
page11+Page number (1-based)
limit501–10,000Items per page
sortid DESC-Sort config as JSON: {"Field":"name","Direction":"ASC"}

Example:

curl "https://api.octopuscards.io/api/v1/products?page=2&limit=25" \
  -H "Authorization: Bearer <token>"

Date Format

All date and time fields in requests and responses use RFC 3339 (ISO 8601 with timezone):

2025-01-15T13:00:00Z
2025-01-15T18:30:00+05:30

Response Envelope

JWT-authenticated API clients (everything in this reference) always get the raw entity as the response body — the fields documented for the endpoint are the top-level keys.

The cookie-authenticated client portal receives the same payload wrapped in {"success": true, "data": <payload>} for compatibility with its session-based code path. SDK and server-to-server integrations only ever see the raw shape.

Error responses share a single shape regardless of vertical:

{
  "error": {
    "name": "ValidationException",
    "code": "VALIDATION_FAILURE",
    "message": "<human-readable message>"
  }
}

Money: denomination vs amount

Two monetary fields appear across the verticals and they mean different things — don't conflate them.

FieldWhere it appearsWhat it means
denominationVoucher products + orders (e.g. an "Amazon $25" card)Face value of a single voucher unit in the product's display currency. Fixed by the catalog — the customer can't pick an arbitrary value, only one of the listed denominations.
amountThe total charge debited from the wallet for the order.For vouchers this is denomination × quantity − discount. For topups it's the customer-requested credit (or, post-charge, the wallet-side total in the wallet's currency). For eSIMs it's the plan price.

Quick rules:

  • Vouchers have both — request with denomination + quantity, response shows denomination, quantity, and the computed amount.
  • Topups have only amount — the customer requests a value directly within the variant's min/max.
  • eSIMs have only amount — the customer picks a fixed-price variant, no separate denomination.

Order identifiers

Every order is identified by two values, the same across every vertical:

FieldIssued byPurpose
idServerNumeric integer, returned in every create response. The canonical handle for GET /…/orders/:id and webhook payloads.
client_referenceYouOptional, ≤ 255 printable-ASCII characters. Acts as your dedup + lookup key: send the same value twice on create and the second call is rejected with 400 "Duplicate client_reference". Echoed back on every list/detail response under the same name.

Use client_reference to make retries safe and to look up your own orders without persisting the server-issued id:

# Look up the order you supplied "order-a8f2c1d9" for at create-time
curl "https://api.octopuscards.io/api/v1/orders?client_reference=order-a8f2c1d9" \
  -H "Authorization: Bearer <token>"

The same ?client_reference= query parameter works on the create-order list endpoint for every vertical. See Idempotency for the full retry pattern.

Pagination Parameter Naming

Two pagination parameter names are in use — also being unified.

VerticalItems-per-page parameter
Voucherslimit (default 50, range 110,000)
Topupsper_page (default 50)
eSIMper_page (default 50)

page (1-based) is consistent across all verticals. Filtering and sort parameters are per-endpoint.

On this page