Errors and fixes

API errors are JSON in the form {"error":"CODE"}.

Error codes

HTTPCodeMeaning
400INVALID_REQUESTA field is malformed, or a field was sent that is not allowed
400IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or malformed
401LIVE_KEY_REQUIREDThe Authorization header is missing or is not a valid ez2_live_... key
401INVALID_API_KEYThe key is unknown or revoked
401INVALID_MCP_TOKENThe MCP token is malformed, expired or revoked
403MCP_FORBIDDENAn MCP token tried a route or scope it is not allowed to use
403MCP_DRAFT_ONLYMCP tried to create a SKU in a game that is not draft, or to set the webhook URL of a game that is not draft or that has orders
404SKU_NOT_FOUNDNo active SKU has this code in this game. pending and suspended SKUs cannot be sold
404ORDER_NOT_FOUNDThe order does not exist or belongs to another game
404GAME_NOT_FOUNDThe game does not exist or is not yours
409IDEMPOTENCY_CONFLICTThis Idempotency-Key was used with different input
409GAME_APPROVAL_REQUIREDThe game is not approved yet
409ORDER_NOT_NOTIFIEDDelivery was acknowledged while the order was not NOTIFYING, NOTIFIED or DELIVERED
409GAME_SLUG_UNAVAILABLEAnother game already uses this slug
409SKU_CODE_EXISTSThis game already has a SKU with this code
413PAYLOAD_TOO_LARGEThe request body is over 16 KiB
415JSON_REQUIREDThe request needs Content-Type: application/json
422UNSAFE_WEBHOOK_URLThe webhook URL is not public HTTPS
500INTERNAL_ERRORAn unexpected error. Try again

Common mistakes

Signature check fails

Your framework parsed the JSON before you read it, so the bytes changed. Read the raw body first. See other frameworks.

If only some webhooks fail, check for player names with non-ASCII characters. Sign the raw bytes, not a re-encoded string.

If every webhook started failing after someone pressed สร้าง secret ใหม่, your server still has the old secret. See Changing the webhook secret.

Clock skew

Your server clock is more than 5 minutes off. Sync it with NTP.

The webhook keeps repeating

Your server answered something other than 200. 201 and 204 count as failed. Answer exactly 200.

Goods delivered twice

You tracked "already granted" in memory, or outside the transaction. Store the order_id in the same database transaction as the grant. See Grant exactly once.

The order is not confirmed paid

The order is not in a paid status, or its sku or buyer differ from the webhook. Do not grant. If the player says they paid, search the order in the Dashboard.

400 when creating an order

You sent a field that is not allowed, such as amount or price. Send only sku, buyer, customer_email and return_url.

409 IDEMPOTENCY_CONFLICT

You reused a key with different input. Each purchase needs a new key. A retry of the same purchase must send the same input.

SKU_NOT_FOUND for a SKU you can see

The SKU is pending or suspended, or it belongs to another game. Only active SKUs can be sold.

The order stays at NOTIFIED

Your acknowledgement failed, for example your server crashed after granting. Check that the goods were granted, then call POST /v1/orders/{id}/delivered. It is safe to call again.

Webhook URL refused

The URL must be HTTPS on port 443, and must resolve to a public address. localhost and private addresses are refused, and redirects are not followed.

Rounding errors in money

0.1 + 0.2 is not 0.3 in floating point. Keep integer satang. amount is a string such as "100.00". Compare it as a string, or convert it to integer satang.

A player paid but the goods did not arrive

  1. The player opens the receipt and contacts your game team.
  2. Search the order in the Dashboard by Order ID or Player ID.
  3. If it is NOTIFY_FAILED, resend the notification.
  4. If you cannot fix it, escalate it to EZ2Pay from the same screen.

Still stuck

Escalate from the order search screen in the Dashboard, and include the Order ID.