PayMeNow

Errors

The error response shape, HTTP status codes and the full list of error codes.

Generated from public-api.mdVersion 1.2.0Last updated 2026-09-20

#Error Responses

All errors follow this format:

json
{
  "code": "ERROR_CODE",
  "message": "Human-readable error message",
  "details": { "reason": "…", "trace_id": "…" }
}

details is optional. When present it may carry reason (a client-safe explanation from the payment service or provider) and trace_id — quote the trace_id when contacting support. The same value is returned in the X-Request-ID response header on every request.

Validation failures on deposit/withdraw instead fill details with one entry per rejected field, keyed by JSON field name, described in Thai.

#HTTP Status Codes

StatusDescription
200Success
201Created (deposit / withdraw)
400Bad request — validation or business rule
401Unauthorized — missing/invalid key, signature or timestamp
403Forbidden — merchant inactive, or IP not whitelisted
404Not found
409Conflict (e.g. an already-processed payment)
413Payload too large (slip upload over 10 MB)
429Rate limit exceeded
500Internal error
502The payment or reporting service could not be reached
503Temporarily unavailable — maintenance window or operational pause
504Gateway timeout (also the sandbox timeout scenario)

#Error Codes

Emitted by the API gateway:

CodeHTTPDescription
MISSING_API_KEY401X-API-Key header missing
MISSING_SIGNATURE401X-Signature header missing
MISSING_TIMESTAMP401X-Timestamp header missing
INVALID_TIMESTAMP401X-Timestamp is not a valid Unix timestamp
TIMESTAMP_EXPIRED401Timestamp outside the ±5-minute window
INVALID_API_KEY401Unknown API key
INVALID_SIGNATURE401Signature verification failed
MISSING_CONTENT_HASH401X-Content-SHA256 absent on a slip upload
UNAUTHORIZED401Merchant could not be resolved from the auth context
AUTHENTICATION_ERROR500Credential lookup failed upstream
MERCHANT_INACTIVE403Merchant account is not active
IP_NOT_WHITELISTED403Caller IP is not in your whitelist
INVALID_BODY400Request body could not be read
INVALID_REQUEST400Request body validation failed
MISSING_PAYER_INFO400Only the payer trio is missing on a deposit
CONTENT_HASH_MISMATCH400Uploaded file does not match X-Content-SHA256
FILE_REQUIRED400Slip upload without a file part
FILE_READ_ERROR400Uploaded file could not be read
FILE_TOO_LARGE413Slip over 10 MB
RATE_LIMIT_EXCEEDED429Per-merchant rate limit exceeded
TIMEOUT504Sandbox timeout scenario
UPSTREAM_UNAVAILABLE502Payment/reporting service unreachable — safe to retry
INTERNAL_ERROR500Unexpected upstream failure (carries trace_id)

Passed through from the payment service:

CodeHTTPDescription
INVALID_AMOUNT400Amount outside the allowed range for your account
INSUFFICIENT_BALANCE400Not enough available balance for the withdrawal + fee
PAYMENT_CANNOT_BE_CANCELLED400Payment is not in pending
INVALID_ACCOUNT400Invalid bank account (sandbox invalid_account scenario)
NOT_ELIGIBLE400Slip upload against a transaction that is not expired
EXPIRED_TOO_OLD400Slip upload more than 24 h after expiry
AMOUNT_MISMATCH, RECEIVER_NOT_RECOGNIZED, SLIP_TOO_OLD, SLIP_ALREADY_USED, PAYER_BANK_MISMATCH, SLIP_INVALID400Slip verification failures
NOT_FOUND404Payment not found, or not yours
CONFLICT409Payment already processed
NO_DEPOSIT_ACCOUNT503No receiving account is available for this deposit right now — none configured, all paused, or all at their daily limit. Retryable; if it persists, contact support (message is in Thai)
SERVICE_UNAVAILABLE503Maintenance window, operational pause, or pay-in/pay-out disabled for your account (message is in Thai)

Error-code stability. The gateway forwards upstream code values verbatim, so the list above is the set observed today rather than a frozen contract. Branch on HTTP status first and treat unknown code values as retryable/non-retryable according to the status.

A 503 always explains itself. Unlike a 500, a 503 is a state the platform is reporting deliberately, so its code and message reach you unchanged and are safe to show an operator. A 500 with no business reason is reduced to a generic message on purpose — its upstream text could be an internal error string.

Codes that appeared in earlier revisions of this document and are not emitted: PAYMENT_NOT_FOUND (it is NOT_FOUND), PAYMENT_ERROR, DUPLICATE_REFERENCE (duplicate reference_id is not detected), INVALID_BANK_CODE (bank codes are only length-checked).