Placing orders
Placing an order is a single call to POST /api/v1/orders. It charges your
account balance and returns 201 immediately with status: "pending".
Fulfillment happens asynchronously — the final result arrives on your
webhook and is always readable from GET /api/v1/orders/{id}.
Item types
Every catalog item has a product type that determines what you must send:
You discover an item’s item_id, product type, quantity bounds and field
definitions from the catalog export — a separate data feed, not part of this
API. The API does not expose a catalog-browsing endpoint.
Request fields
item_id(integer, required) — the numeric item id from the catalog.quantity(number) — see the item-type table above. Must fall within the item’s min/max range.key: an integer count (defaults to1).topup: a decimal amount (required).service: omit it — it is always1.
merchant_order_id(string, optional) — your own reference id, unique per account. Use it for idempotency and to correlate webhook events with your records.fields(object) — required fortopupandserviceitems only, keyed by the item’s field names.keyitems take nofields.
Field values: text vs choice
Each item field is either a text field or a choice field:
- Text field → send the value as a string (e.g.
"player_id": "123456789"). - Choice field → send the numeric choice id, not the label (e.g.
"server": 17, where17is the id of the “Europe (EU-West)” choice).
In responses, submitted fields are echoed back with choice values rendered as
their human-readable display name (e.g. "server": "Europe (EU-West)"),
even though you submitted the numeric id. This is display-only — keep sending
numeric choice ids on create.
Creating an order
cURL
Python
A successful create returns 201 with the order in pending:
The order lifecycle
An order starts pending, may briefly be processing, and then settles on one
of three terminal statuses:
codes— delivered key strings, forkeyitems.refund_amount— the amount refunded for undelivered units on apartialorfailedorder.error/error_message— set whenstatusisfailed(e.g.error: "OUT_OF_STOCK").
An item being out of stock at create time is rejected up front as a 400
validation error (“Item is not available”). If stock or fulfillment fails
after the order is accepted, the order settles as a terminal
status: "failed" (surfaced on your webhook) — not an HTTP error. See
Errors.
Reading an order
Fetch the current state of any of your orders with GET /api/v1/orders/{id}:
cURL
Python
GET /api/v1/orders/{id} returns 404 (code: "not_found") for an order that
does not exist or is not owned by your account. There is no
list-orders endpoint in the API — browse orders in the dashboard.
Webhook vs polling
Prefer the webhook: Voodoo Center pushes the terminal event to your
URL as soon as the order settles — no polling needed. Polling
GET /api/v1/orders/{id} is a fine fallback (e.g. if a webhook delivery was
missed), but the webhook is the lower-latency, lower-load path.