Errors
Error envelope, types, codes, and retry rules.
Errors
Envelope
Failed requests return:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_amount",
"message": "Amount must be a positive integer in rupiah, without decimals.",
"param": "amount"
},
"meta": {
"request_id": "req_01J8Z3K9QW"
}
}Branch on error.code, never on error.message. Message wording may change.
error.param names the first offending field when validation fails.
HTTP status
| Range | Meaning |
|---|---|
| 4xx | Fix the request. Do not retry unchanged. |
| 5xx | Transient failure. Retry with exponential backoff. |
| 429 | Rate limited. Wait Retry-After seconds, then retry. |
provider_error means an upstream payment provider failed, not Momen — these are safe to retry.
Request ID
Every response (success or error) includes meta.request_id and an X-Request-Id header with the same value. Quote it to support.
Error types
From the OpenAPI ErrorResponse schema:
type | Meaning |
|---|---|
authentication_error | Missing, malformed, or revoked key |
permission_error | Key valid but not permitted (IP, account, sandbox-only, …) |
invalid_request_error | Malformed or conflicting request |
idempotency_error | Idempotency key problems |
rate_limit_error | Too many requests |
api_error | Unexpected API failure |
provider_error | Upstream payment provider failure — retry |
Error codes found in the contract
Codes documented in openapi-public-v1.yaml (examples and prose):
code | Typical HTTP | Notes |
|---|---|---|
invalid_api_key | 401 | Key missing, malformed, or revoked |
api_key_wrong_environment | 401 | sk_test_ on production URL or vice versa |
public_key_not_accepted | 401 | pk_ keys are not accepted yet |
ip_not_allowed | 403 | Live IP allowlist miss (sandbox ignores IPs) |
sandbox_only | 403 | Sandbox endpoint called with a live key |
invalid_amount | 422 | Amount not a positive integer rupiah |
idempotency_key_reused | 422 | Same key, different body |
idempotency_request_in_progress | 409 | Original request still running — retry shortly |
payment_link_not_cancelable | 409 | Link already succeeded or expired |
payment_link_terminal | 409 | Simulate against a terminal link |
insufficient_balance | — | Appears as the schema example for error.code |
Rate limit headers
Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. A 429 also carries Retry-After in seconds.