Get one order

GET/api/v1/orders/{order_id}
Required scope:orders:read

Read the state and the result of one order. This is the source of truth for order state, so check here rather than relying on callbacks alone: a callback is a convenience that tells you when something changed, but if one never reaches you the real answer is still readable here. Three fields carry the whole answer: status = how far the order has got · charged = whether the wallet was actually charged · delivery = the goods, which appear once status is completed. The shape of delivery depends on the type of that order — cdkey/account1 return keys, account2 returns account credentials.

delivery has two shapes, and the type on the same order decides which

The shape of delivery is decided by the type field on the same order, not by the endpoint you called and not by the status — the two shapes have no field in common whatsoever: cdkey and account1 give you { keys: [{ serial }] }, while account2 gives you login · password · email · email_password · extra · Branch your parser on type and nothing else, never on the contents — branching the wrong way reads undefined across the board, which looks exactly like the goods not having arrived even though you paid and they did · The same rule applies to the object a webhook delivers, because it is this very object.

The authentication refusals reach this endpoint too

The five refusals at the authentication gate — invalid_key (401) · key_revoked (401) · insufficient_scope (403) · quota_exceeded (429) · too_many_inflight (429) — can happen on every endpoint, this one included, because they are thrown before the request reaches a single line of this endpoint's own logic (the scope it needs is the one declared at the top of this page). The response tabs above therefore list only what this endpoint itself answers; that is not a claim that those five cannot happen here. All of the detail lives in one place, on the identity endpoint page at /developers/access/me: the HTTP status of each one, which of them spend your daily quota, which headers come back, and why the two 429s need opposite handling. It is deliberately not copied onto every page, because the second copy is the one nobody remembers to update the day the rules change.

Request parameters

Path

NameTypeHow to use it
order_idRequiredstring
The order_id you were given when you placed the order
Details and caveats
  • ord_ followed by 24 hexadecimal characters
  • Record it on your side when you order: it is the only way back to that order directly.

Header

NameTypeHow to use it
AuthorizationRequiredstring
Your key as Bearer <key> — required on every request.

Response fields

NameTypeHow to use it
order_idstring or null
The order id: ord_ followed by 24 hexadecimal characters
Details and caveats
  • Like every field in this table it can be null, including the ones that identify the order, so allow null everywhere in your parser
  • A stored value we cannot read comes back as null rather than as a guess or a zero, so a strict schema that declares any of them non-nullable will throw on an order whose data got damaged
  • which is exactly the order you most need to read
  • Treat a null order_id or status as a case for a human, not as a value to branch on.
statusstring or null
Where the order stands: queued = accepted, waiting for us to buy it (account2 only)
Details and caveats
  • processing = in progress, nothing recorded on the order yet
  • completed = finished, delivery is an object
  • failed = unsuccessful, read error.code
  • refunded = refunded afterwards, with delivery back to null even if it had been handed over
  • ⚠️ processing can be permanent: a cdkey or account1 order can stop moving there for good, even though it has been paid for and the goods may already have arrived
  • read it again periodically, and if it has not moved for a long time contact us with the order_id (the same way out as an order that is completed with an empty delivery.keys), and never place a new order
  • **These five are the values that exist today, not a closed set
  • new order statuses can be added at any time without notice**, exactly like new values of data.code
  • Always keep a fallback branch for a value you do not recognise, never branch without a default case, and do not type this field as a closed set in your parser: the day a sixth status ships, a parser like that throws on an order that has already been paid for
  • the one order you most need to be able to read
  • An unrecognised value must not be treated as success and must not be treated as failure: treat the order as not finished, read it again, and if nothing changes put it in front of a human with the order_id
  • never submit it again, because submitting again means paying twice.
typestring or null
The type you ordered — this is what tells you which shape delivery takes, not the endpoint you called and not the status.
product_idstring or null
The product you ordered.
pricenumber or null
The agreed price in THB, equal to the expected_price you sent when ordering.
chargedobject or null
{ amount, currency }, what was actually taken from your wallet, and the only field that answers whether this order was paid for: null while the order carries no record of a charge, an object once it does
Details and caveats
  • A refund neither clears nor rewrites it, so you can still see what was taken; the thing that tells you an order was refunded is status being refunded
  • charged.currency is your wallet's currency while price is always THB, so for a wallet that is not in THB the two values naturally differ
  • do not reconcile by comparing these two fields directly
  • The field only appears once both the amount and the currency were recorded; half of a record counts as none.
deliveryobject or null
The goods, present only while status is completed, in one of two shapes that share no field at all, chosen by the type of this same order
Details and caveats
  • The keys shape (cdkey / account1) is { keys: [{ serial }] }, where keys is an array carrying at most one entry per order today and serial is a string issued by the distributor
  • depending on the product it may be a key or a redemption link, so store and display it as given and do not try to parse its format
  • The account shape (account2) is login
  • password
  • email
  • email_password
  • extra, where the first four are the credentials of the account and of the mailbox attached to it, and anything that could not be read is null rather than absent
  • extra describes the account itself and its field set differs per platform, so read the keys you know and ignore the rest.
errorobject or null
Why it did not succeed.
Details and caveats
  • Present only while status is failed, carrying { code, message }, and its code must always be read together with charged, because the same code can reach you at different moments
  • at some of them no money has moved, at others it moved and came back
  • The vocabulary is the same one the ordering endpoint answers with live (see the table on /developers/orders/create), but it reaches you at a different moment.
created_atISO 8601 string (UTC) or null
When the order was created.
updated_atISO 8601 string (UTC) or null
When the order was last modified.

Possible statuses

StatusCodeMeaning
200
queued — not its turn yet, read it again later
We have not started buying this one
Details and caveats
  • charged and delivery are both null, which is correct for this status
  • All you do is wait a while and read it again (or wait for the callback, if you configured one).
200
failed — unsuccessful, and the order carries no record of a charge
Read the reason from error.code
Details and caveats
  • This one has charged as null, meaning the order carries no record of a charge at all
  • Once you have addressed whatever error.code points at, you can order again under a fresh request_id.
200
failed — unsuccessful, but charged holds a value
This order and the one above carry the very same status while the money got different distances
Details and caveats
  • the one above has no record of a charge, this one has
  • That is exactly why reconciliation reads charged and nothing else: not status, not error.code.
200
refunded — refunded, with charged left exactly as it was
A refund neither clears nor rewrites charged, so it still tells you what was taken; the thing that tells you an order was refunded is status being refunded
Details and caveats
  • An order that had been handed over no longer carries a delivery once it is refunded.
200
completed — delivered (the keys shape)
type is cdkey or account1, so the goods are at delivery.keys[].serial, which is the value you pass straight on to your buyer.
200
completed — but delivery.keys is empty (not normal)
An empty keys together with completed means the key has not been recorded on the order yet
Details and caveats
  • Read the order again and do not place a new order; if it stays empty, contact us with the order_id.
200
completed — delivered (the account shape)
type is account2, so the goods are the four credential fields, and those are what you hand to your buyer
Details and caveats
  • extra describes the account itself: its field set differs per platform and it can be an empty object, so never require a particular key to be present
  • The extra in the example is abbreviated to keep it readable: a real one carries the whole set of fields defined for that platform, which for Steam is around two dozen keys. A platform we have not defined a field set for yet yields an extra holding item_id alone
  • treat it as free-form: read the keys you know and ignore the rest
  • extra.alternate_email appears when the account genuinely has a second, different mailbox.
200
completed — but the account details cannot be read (not normal)
An order that is completed while its details are still incomplete keeps this same shape
Details and caveats
  • every key present with null values; delivery as a whole never becomes null
  • If you see this, read the order again; if it does not change, contact us with the order_id
  • do not place a new order.
400bad_request
No order_id in the URL
The order_id segment of the URL is empty, or holds only whitespace — put a real value in it and send the request again.
404order_not_found
No such order
Three cases reach this: a malformed order_id, an order that does not exist, or an order belonging to another reseller
Details and caveats
  • all three answer with this exact body, field for field. That is deliberate: guessing at other people's order_id values must not reveal which ones are real.
Last updated 2026-09-07