API ReferenceVouchers
Create Order
Place a voucher order and receive gift card codes
POST /api/v1/orders
Create a new voucher order. Octopus Cards validates your request, checks wallet balance, deducts funds, and fulfills the order - either immediately (sync) or in the background (async).
Request
curl -X POST "https://api.octopuscards.io/api/v1/orders" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"product_id": 123,
"denomination": 50.00,
"quantity": 5,
"client_reference": "MY_ORDER_001",
"email": "recipient@example.com"
}'Request Parameters
| Key | Type | Required | Description |
|---|---|---|---|
product_id | integer | Yes | Product ID from the catalog. Must be an active, non-blacklisted product. |
denomination | number | Yes | Face value per voucher. Must be greater than 0 and fall within an available denomination range for the product. |
quantity | integer | Yes | Number of vouchers to order. Minimum 1, maximum is your client's bulk limit (check max_quantity from the Charges endpoint). Absolute cap: 5,000 per request. |
wallet_id | integer | No | Wallet to debit. If omitted, Octopus Cards uses your wallet matching the product's currency. Specify this for cross-currency orders (triggers FX conversion). |
client_reference | string | No | Your dedup + lookup key (printable ASCII, max 255 characters). Unique per client — a duplicate (client_id, client_reference) returns 400 "Duplicate client_reference". Optional, but required for retry-safe order creation. See Idempotency. |
email | string | No | Recipient email address. Used for voucher delivery notifications. Must be a valid email format (max 254 characters). |
Response (Sync - quantity ≤ 5)
When quantity is 5 or fewer, vouchers are delivered immediately:
{
"id": 1234,
"product_id": 123,
"product_name": "Steam Wallet Card",
"denomination": 50.00,
"quantity": 5,
"amount": 250.00,
"discount": 8.75,
"client_reference": "MY_ORDER_001",
"wallet_id": 1,
"status": "DELIVERED",
"base_currency": "USD",
"deduction_currency": "USD",
"message": "Order created successfully",
"email": "recipient@example.com",
"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 (Async - quantity > 5)
For larger orders, the response returns immediately with PENDING status and no vouchers:
{
"id": 1235,
"product_id": 123,
"product_name": "Steam Wallet Card",
"denomination": 50.00,
"quantity": 100,
"amount": 5000.00,
"discount": 175.00,
"client_reference": "BULK_ORDER_001",
"wallet_id": 1,
"status": "PENDING",
"base_currency": "USD",
"deduction_currency": "USD",
"message": "Order created successfully",
"email": "recipient@example.com",
"vouchers": []
}Poll GET /api/v1/orders/1235 to check when delivery is complete.
Response (Cross-currency order)
When paying from a different currency wallet:
{
"id": 1236,
"product_id": 456,
"product_name": "Google Play Gift Card (UK)",
"denomination": 25.00,
"quantity": 2,
"amount": 50.00,
"discount": 1.50,
"client_reference": "FX_ORDER_001",
"wallet_id": 2,
"status": "DELIVERED",
"base_currency": "GBP",
"deduction_currency": "EUR",
"message": "Order created successfully",
"vouchers": [
{
"card_number": "GPLAY-XXXX-XXXX",
"pin_code": null,
"claim_url": "https://play.google.com/redeem?code=XXXX",
"expires_at": "2027-06-01T00:00:00Z",
"voucher_reference_number": null
}
]
}base_currency is the product's currency (GBP). deduction_currency is the wallet's currency (EUR). The FX conversion details are recorded in the wallet transaction.
Response Fields
| Key | Type | Description |
|---|---|---|
id | integer | Unique order identifier |
product_id | integer | Product that was ordered |
product_name | string | Product display name |
denomination | number | Face value per voucher |
quantity | integer | Number of vouchers ordered |
amount | number | Total face value (denomination × quantity) |
discount | number | Total discount applied |
client_reference | string | Echo of the value you supplied on create (omitted if you didn't supply one) |
wallet_id | integer | Wallet that was debited |
status | string | Order status - see Order Statuses |
base_currency | string | Product's currency code |
deduction_currency | string | Wallet's currency code (differs from base_currency in cross-currency orders) |
message | string | Human-readable status message |
email | string | Delivery email (if provided) |
vouchers | array | Delivered vouchers (empty if PENDING) |
Voucher Fields
| Key | Type | Description |
|---|---|---|
card_number | string or null | Voucher code / gift card number |
pin_code | string or null | PIN for the voucher |
claim_url | string or null | Redemption URL (for URL-based delivery) |
expires_at | string or null | Voucher expiry date (RFC 3339) |
voucher_reference_number | string or null | Brand reference for the voucher |
What Happens Step by Step
- Validate - request fields, product existence, denomination range
- Check duplicates - reject if
client_referencewas already used by this client - Select wallet - use
wallet_idor find the wallet matching the product's currency - Calculate charges - apply discounts and FX conversion (same as the charges endpoint)
- Check balance - verify wallet has enough funds
- Debit wallet - atomically deduct the total payable amount and create a transaction record
- Create order - insert the order record
- Fulfill - for sync orders (≤ 5 qty), fetch voucher codes immediately. For async (> 5 qty), queue for background processing.
- Respond - return the order with vouchers (sync) or pending status (async)
Errors
400 Bad Request - Missing or invalid required fields.
{
"error": {
"name": "ValidationException",
"code": "VALIDATION_FAILURE",
"message": "Invalid product_id: required"
}
}