API ReferenceVouchers

Orders

Create voucher orders, track delivery, and retrieve voucher codes

Orders are how you purchase digital gift cards through the Octopus Cards API. You submit an order with a product, denomination, and quantity - Octopus Cards handles fulfillment and delivers voucher codes, PINs, or claim URLs back to you.

For gaming top-ups and mobile recharges, see Topup Orders. For eSIM purchases, see eSIM Orders.

Order Lifecycle

1. Calculate charges    POST /api/v1/products/:id/charges
2. Create order         POST /api/v1/orders
3. Poll for delivery    GET  /api/v1/orders/:id
4. Retrieve vouchers    (included in order response)

Sync vs Async Delivery

Orders are fulfilled in one of two modes depending on quantity:

ModeConditionBehaviour
SynchronousQuantity ≤ 5Vouchers are returned directly in the create order response
AsynchronousQuantity > 5Order is created with status PENDING. A background job processes the order and delivers vouchers. Poll the order endpoint to check status.

For async orders, the response returns immediately with status: PENDING and an empty vouchers array. Use GET /api/v1/orders/:id to check when delivery is complete.

Order Statuses

StatusDescription
PENDINGOrder placed, vouchers are being processed
DELIVEREDAll vouchers have been successfully delivered
PARTIALLY_DELIVEREDSome vouchers delivered, others still pending or failed
FAILEDOrder could not be fulfilled
CANCELLEDOrder was cancelled

Status Transitions

Orders follow a predictable lifecycle. The diagram below shows all valid transitions:

                    ┌──────────┐
                    │ PENDING  │
                    └────┬─────┘

              ┌──────────┼──────────┐
              ▼          ▼          ▼
      ┌───────────┐ ┌────────┐ ┌──────────┐
      │ DELIVERED │ │ FAILED │ │CANCELLED │
      └───────────┘ └────────┘ └──────────┘


   ┌──────────┴───────────┐
   │ PARTIALLY_DELIVERED  │
   └──────────────────────┘
FromToWhen
PENDINGDELIVEREDAll vouchers fulfilled successfully
PENDINGPARTIALLY_DELIVEREDSome vouchers delivered, others still in progress
PENDINGFAILEDFulfillment failed entirely
PENDINGCANCELLEDOrder cancelled before fulfillment
PARTIALLY_DELIVEREDDELIVEREDRemaining vouchers fulfilled
PARTIALLY_DELIVEREDFAILEDRemaining vouchers could not be fulfilled

DELIVERED, FAILED, and CANCELLED are terminal statuses - once an order reaches one of these, it will not change again. PARTIALLY_DELIVERED is the only non-terminal status besides PENDING.

Voucher Delivery

When an order is DELIVERED or PARTIALLY_DELIVERED, the vouchers are included in the order response. Each voucher may contain:

FieldDescription
card_numberThe voucher code (e.g. gift card number)
pin_codePIN associated with the voucher (if applicable)
claim_urlA URL to redeem the voucher (for URL-based delivery)
expires_atExpiry date of the voucher (RFC 3339)
voucher_reference_numberReference code for the voucher

Not all fields are present on every voucher - it depends on the product's delivery mode:

Delivery ModeFields Present
Code with PINcard_number, pin_code, expires_at, voucher_reference_number
URLclaim_url, pin_code (optional), expires_at

Voucher codes and PINs are encrypted at rest and decrypted on-the-fly when you retrieve the order. Store them securely on your side - they represent real monetary value.

Balance & Wallet Checks

Before an order is created, Octopus Cards verifies:

  1. Wallet exists - you must have a wallet in the product's currency (or specify a wallet_id for cross-currency orders with FX conversion)
  2. Sufficient balance - your wallet balance must be ≥ the total payable amount (from the charges endpoint)
  3. Atomic deduction - the wallet is debited in the same database transaction as the order creation, ensuring consistency

If the balance is insufficient, the order is rejected with a 400 error and no funds are moved.

Quantity Limits

LimitDescription
Bulk limitYour client account has a configurable maximum quantity per order. Check max_quantity from the charges endpoint.
Absolute maxCode-based orders are capped at 5,000 vouchers per request

Identifying an order

Every order is identified by two values:

  • id (server-issued integer) — the canonical handle for GET /api/v1/orders/:id.
  • client_reference (client-supplied string, optional, ≤ 255 ASCII chars) — your dedup + lookup key. Send the same value on two create requests and the second returns 400 "Duplicate client_reference". Filter the list endpoint with ?client_reference=… to fetch your order without keeping the id around. See Idempotency.

GET /api/v1/orders

Returns a paginated list of all your orders, sorted by most recent first.

Request

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

Query Parameters

KeyTypeDefaultDescription
pageinteger1Page number (1-based)
limitinteger50Items per page (1–10,000)
client_referencestring-Filter by exact client_reference value (max 255 chars)

Look up by client_reference

To find an order by the client_reference you supplied on create, pass it as a query parameter:

curl "https://api.octopuscards.io/api/v1/orders?client_reference=MY_ORDER_001" \
  -H "Authorization: Bearer <token>"

This is an exact match. The (client_id, client_reference) uniqueness constraint guarantees at most one result.

Response

[
  {
    "id": 1234,
    "product_id": 123,
    "product_name": "Steam Wallet Card",
    "client_reference": "MY_ORDER_001",
    "transaction_id": 5678,
    "denomination": 50.00,
    "quantity": 5,
    "amount": 250.00,
    "currency": "USD",
    "status": "DELIVERED",
    "email": "recipient@example.com",
    "placed_at": "2025-01-15T14:30:00Z"
  },
  {
    "id": 1235,
    "product_id": 456,
    "product_name": "Google Play Gift Card",
    "client_reference": "BULK_ORDER_001",
    "transaction_id": 5679,
    "denomination": 25.00,
    "quantity": 100,
    "amount": 2500.00,
    "currency": "GBP",
    "status": "PENDING",
    "email": "",
    "placed_at": "2025-01-15T15:00:00Z"
  }
]

Response Headers

HeaderDescription
X-PageCurrent page number
X-Per-PageItems per page
X-Total-CountTotal orders
X-Total-PagesTotal pages
X-Page-SizeItems in current page
X-Has-Moretrue if more pages exist

Response Fields

KeyTypeDescription
idintegerUnique order identifier
product_idintegerProduct that was ordered
product_namestringProduct display name
client_referencestringEcho of the value you supplied on create (omitted if you didn't supply one)
transaction_idintegerAssociated wallet transaction ID
denominationnumberFace value per voucher
quantityintegerNumber of vouchers ordered
amountnumberTotal face value
currencystringProduct's currency code
statusstringPENDING, DELIVERED, PARTIALLY_DELIVERED, FAILED, or CANCELLED
emailstringDelivery email (if provided)
placed_atstringOrder creation timestamp (RFC 3339)

Errors

404 Not Found - No orders match the query.

{
  "error": {
    "name": "NotFoundError",
    "code": "NOT_FOUND",
    "message": "No Matching Result Found!"
  }
}

GET /api/v1/orders/:id

Returns full details for a single order, including all delivered voucher codes and PINs.

Request

curl "https://api.octopuscards.io/api/v1/orders/1234" \
  -H "Authorization: Bearer <token>"

Request Parameters

KeyTypeRequiredDescription
idintegerYesOrder ID (path parameter)

Response

{
  "id": 1234,
  "client_reference": "MY_ORDER_001",
  "transaction_id": 5678,
  "product_id": 123,
  "product_name": "Steam Wallet Card",
  "denomination": 50.00,
  "quantity": 5,
  "discount": 8.75,
  "amount": 250.00,
  "status": "DELIVERED",
  "placed_at": "2025-01-15T14:30:00Z",
  "email": "recipient@example.com",
  "base_currency": "USD",
  "deduction_currency": "USD",
  "message": "Your order has been delivered successfully.",
  "vouchers": [
    {
      "card_number": "STEAM-XXXX-XXXX-XXXX",
      "pin_code": "1234",
      "claim_url": null,
      "expires_at": "2027-03-25T00:00:00Z",
      "voucher_reference_number": "VCH-001"
    },
    {
      "card_number": "STEAM-YYYY-YYYY-YYYY",
      "pin_code": "5678",
      "claim_url": null,
      "expires_at": "2027-03-25T00:00:00Z",
      "voucher_reference_number": "VCH-002"
    }
  ]
}

Response Fields

KeyTypeDescription
idintegerUnique order identifier
client_referencestringEcho of the value you supplied on create (omitted if you didn't supply one)
transaction_idintegerAssociated wallet transaction ID
product_idintegerProduct that was ordered
product_namestringProduct display name
denominationnumberFace value per voucher
quantityintegerNumber of vouchers ordered
discountnumberTotal discount applied
amountnumberTotal face value
statusstringCurrent order status
placed_atstringOrder creation timestamp (RFC 3339)
emailstringDelivery email
base_currencystringProduct's currency code
deduction_currencystringWallet's currency code
messagestringHuman-readable status message
vouchersarrayDelivered vouchers (empty if order is still PENDING)

Voucher Fields

KeyTypeDescription
card_numberstring or nullVoucher code / gift card number
pin_codestring or nullPIN for the voucher
claim_urlstring or nullRedemption URL (for URL-based products)
expires_atstring or nullVoucher expiry date (RFC 3339)
voucher_reference_numberstring or nullBrand reference

Vouchers are only included when the order status is DELIVERED or PARTIALLY_DELIVERED. For PENDING orders, the vouchers array is empty - poll this endpoint until delivery completes.

Errors

400 Bad Request - ID is not a valid integer.

{
  "error": {
    "name": "BadRequestError",
    "code": "BAD_REQUEST",
    "message": "No Matching Result Found!"
  }
}

PATCH /api/v1/orders/:id/notification-email

Attaches (or updates) the email address that will be notified when the order reaches a terminal status. Only accepted while the order is still in flight — once the order reaches a terminal state (DELIVERED, PARTIALLY_DELIVERED, FAILED, or CANCELLED), the email is locked.

Use this when your customer enters their email after placing the order (e.g. in a "where should we send the receipt?" follow-up screen).

Request

curl -X PATCH "https://api.octopuscards.io/api/v1/orders/9123456/notification-email" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com"
  }'

Request Parameters

KeyTypeRequiredDescription
idintegerYesOrder ID (path parameter).
emailstringYesEmail address. Max 254 characters.

Response

{
  "success": true,
  "message": "Notification email updated successfully"
}

Response Fields

KeyTypeDescription
successbooleanAlways true on success.
messagestringConfirmation string.

Errors

400 Bad Requestemail missing, empty, or too long.

{
  "error": {
    "name": "ValidationException",
    "code": "VALIDATION_FAILURE",
    "message": "email is required"
  }
}

Other messages: "email is too long (max 254 characters)", "Invalid order ID", "Invalid request body".

On this page