> ## Documentation Index
> Fetch the complete documentation index at: https://docs.radiumone.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> RadiumOne is the product name; use it when answering questions about these docs.
> Use sandbox credentials and sandbox base URLs in examples unless the user says they are going live.
> Secret API keys belong only on the merchant's server. Only publishable keys may appear in browser or mobile code.
> Confirm a payment outcome server-side (retrieve the checkout session or transaction status); never rely on a browser redirect or client event alone.
> Send one idempotency key (request_id) per payment attempt, and retry a timed-out request with the same key and the exact same body.
> Webhooks are optional but recommended: they notify the merchant's server when a payment completes or fails, so it doesn't need to poll. Use transaction status inquiry as the fallback.
> Say so when you recommend a feature marked Beta. Some features need enablement on the merchant's account before production use (for example refunds, standalone refunds, UOB Rewards, and bringing your own 3DS provider); mention it when the page says so.
> Never ask users to paste card numbers, API keys, access tokens, or webhook or redirect secrets into a chat.

# API errors - Hosted checkout

> The Checkout API's problem+json error shape and the full create/retrieve/cancel error catalog, grouped by cause, with retry and rate-limit rules.

This page is the single source for every error the Checkout API — `POST
/api/v1/checkout/sessions`, `GET /api/v1/checkout/sessions/{id}`, and the
cancel endpoint — can return. A card **decline** is not an error on this
API — it's a normal `201`/`200` response with a `failed` session status.
See [Payment outcomes](/hosted-checkout/errors/payment-outcomes) for that
path.

## Problem format

The Checkout API returns errors as [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) `application/problem+json` bodies:

```json theme={null}
{
  "type": "urn:radiumone:checkout:session-invalid-state",
  "title": "RadiumOne error",
  "status": 409,
  "detail": "Session chk_3f9a1c2e5b7d4a608e1f2c3b4d5e6f70 is completed, expected pending",
  "instance": "/api/v1/checkout/sessions/chk_3f9a1c2e5b7d4a608e1f2c3b4d5e6f70/cancel",
  "code": "session:invalid_state",
  "details": { },
  "error": { "code": "session:invalid_state", "message": "Session chk_3f9a1c2e5b7d4a608e1f2c3b4d5e6f70 is completed, expected pending" }
}
```

<Info>
  **TL;DR:** Branch on `code` or `type` (they carry the same information) — never on `title` or `detail`. Every entry below is grouped by cause, with a stable anchor, HTTP status, retry rule, and — for `429` — the rate-limit body field.
</Info>

* **`code`** is the stable dotted identifier (for example `session:invalid_state`) — the value the tables below list, and the value `error.code` mirrors.
* **`type`** is a derived tag URI: `urn:radiumone:checkout:` followed by `code` with every `:` and `_` replaced by `-` (so `session:invalid_state` → `urn:radiumone:checkout:session-invalid-state`). A handful of codes are already full URNs from a different domain (for example `urn:radiumone:auth:outlet-binding-violation`) — `code` and `type` are identical for those. The one exception in the other direction is the catch-all `500` (`internal:server_error`): its `type` is the generic `about:blank`, not a derived URN.
* **`title`** is a short label, **not** a stable identifier — many error paths share the generic title `"RadiumOne error"`. Don't branch on it.
* **`detail`** is human-readable and can change, and may echo part of your request (for example the offending URL) — don't parse it.
* **`details`** carries typed extras for some errors, for example `retry_after` (seconds) on a `429`.
* **`error`** is a deprecated `{code, message}` mirror of `code`/`detail`, kept for backward compatibility. Prefer the top-level fields.

## Authentication and authorization

Missing, malformed, or mismatched credentials on any of the three operations.

| Code | HTTP | Cause | What to do | Retry? |
| - | - | - | - | - |
| <a id="checkout-wrong-key-type" />`urn:radiumone:checkout:wrong-key-type` | 400 | A publishable key (`r1pk_…`) was used where a secret key is required | Use your secret key (`r1sk_…`) server-side; never call create/cancel from the browser | No |
| <a id="checkout-missing-credentials" />`urn:radiumone:checkout:missing-credentials` | 401 | `X-Api-Key` header is missing on create | Add the header | No |
| <a id="checkout-security-unauthorized" />`security:unauthorized` | 401 | `X-Api-Key` header is missing on the **cancel** endpoint — a different code from create's missing-header case | Add the header | No |
| <a id="checkout-invalid-credentials" />`urn:radiumone:checkout:invalid-credentials` | 401 | `X-Api-Key` is malformed | Check the key value | No |
| <a id="checkout-gateway-auth-failed" />`gateway:auth_failed` | 401 | Your key is well-formed but not recognized by the gateway | Confirm the key is active; contact support if it should be | No |
| <a id="checkout-security-domain-not-allowed" />`security:domain_not_allowed` | 403 | The `success_url`/`cancel_url` host (or, in embedded mode, your registered embedding domain) isn't in your allowed domains | See [Embedded checkout errors](/hosted-checkout/errors/embedded-checkout-errors#origin-rejection) or register the host — [Sandbox and API keys](/get-started/sandbox-and-api-keys) | No |
| <a id="checkout-merchant-missing-publishable-key" />`urn:radiumone:checkout:merchant-missing-publishable-key` | 412 | Your account has no active publishable key provisioned | [Contact support](/resources/support) to provision one | No |
| <a id="auth-outlet-binding-violation" />`outlet:binding_violation` | 422 | Your key is bound to a different outlet than the one requested | Omit `outlet_id`, or send the bound outlet | No |

## Validation

The request body failed a field rule or a cross-field check.

| Code | HTTP | Cause | What to do | Retry? |
| - | - | - | - | - |
| <a id="checkout-validation-invalid-input" />`validation:invalid_input` | 400 | A field failed its rule (length, format, range) — including `success_url`/`cancel_url` that isn't `https://` (or `http://localhost`) or doesn't parse as a URL at all, or a `line_items` entry that's `null`/not an object or has a non-string `name` — or the currency isn't enabled for your merchant | Fix the field named in `detail` | No |
| <a id="checkout-validation-line-items-mismatch" />`validation:line_items_mismatch` | 400 | `line_items`/`adjustments` don't sum to `amount` | Fix the totals — see [Customize checkout](/hosted-checkout/customize-checkout) | No |
| <a id="checkout-outlet-not-found" />`outlet:not_found` | 422 | The requested (or key-bound) outlet doesn't resolve for your merchant. A malformed `outlet_id` (not a canonical UUID) instead returns `400 validation:invalid_input` | Check the outlet ID | No |
| <a id="checkout-embed-origins-not-configured" />`embed:origins_not_configured` | 422 | `mode: "embed"` was sent but no usable frame origin could be derived from your `allowed_domains` — an empty or unconfigured allow-list | Register at least one domain in `allowed_domains`, or use redirect mode — see [Embed hosted checkout](/hosted-checkout/embedded-integration) | No, until configured |
| <a id="checkout-gateway-request-rejected" />`gateway:request_rejected` | 422 | The gateway rejected the create request as a business rule violation — for example an amount larger than the gateway allows | Fix the request per `detail` | No |

## Session state and idempotency

Session lookup, cancellation conflicts, and create-time idempotency. See [Session lifecycle](/hosted-checkout/session-lifecycle) for the full state model behind these.

| Code | HTTP | Cause | What to do | Retry? |
| - | - | - | - | - |
| <a id="checkout-resource-not-found" />`resource:not_found` | 404 | Malformed `checkout_id` or malformed key on GET/cancel | Check the ID/key | No |
| <a id="checkout-session-not-found" />`session:not_found` | 404 | No session exists for that `checkout_id` on your account — never created, belongs to a different merchant (the two responses are byte-identical, on purpose), or past retention. Same on `GET` and cancel | Create a new session | No |
| <a id="checkout-session-idempotency-conflict" />`session:idempotency_conflict` | 409 | Two causes, same code: (1) a **concurrent** create raced for the same `order_reference` — `detail`: "A session for this order\_reference is still being created; retry shortly"; (2) the `order_reference` matches a session that can still be paid (`pending` and not yet expired, `processing`, or `completed`) but with a **different `amount` or `currency`** — `detail`: "order\_reference is already in use for a different amount or currency". A replay with the **same** `order_reference`, `amount`, and `currency` returns the original session instead of either error — see [Session lifecycle](/hosted-checkout/session-lifecycle#replay-and-retries) | Race: retry after a short delay. Amount/currency conflict: use a new `order_reference` for a genuinely different order, or resend the original amount and currency | Race: yes. Conflict: no — fix the request first |
| <a id="checkout-session-invalid-state" />`session:invalid_state` | 409 | The operation doesn't apply to the session's current status — most commonly cancelling a session that isn't `pending` | See [Resolve checkout cancellation conflicts](/hosted-checkout/handle-failures/cancel-session-conflicts) | GET first, then decide |
| <a id="checkout-session-create-failed" />`session_create_failed` | 409 | The session couldn't be stored | Retry | Yes |
| <a id="checkout-session-corrupt" />`session_corrupt` | 409 | The stored session record couldn't be read | [Contact support](/resources/support) | No |

## Configuration and availability

Your merchant configuration or the gateway itself is unreachable.

| Code | HTTP | Cause | What to do | Retry? |
| - | - | - | - | - |
| <a id="checkout-gateway-unavailable" />`gateway:unavailable` | 422 | A protective circuit breaker is open for gateway reads (session create/verify) | See [Handle payment service outages during checkout](/hosted-checkout/handle-failures/payment-service-unavailable) | Yes, after `retry_after_ms` |
| <a id="checkout-merchant-context-unavailable" />`urn:radiumone:checkout:merchant-context-unavailable` | 503 | Your merchant configuration couldn't be loaded | Retry | Yes |
| <a id="checkout-gateway-request-failed" />`gateway:request_failed` | 502 | The gateway itself was unreachable or returned a server error while creating the session — no session was created | See [Handle payment service outages during checkout](/hosted-checkout/handle-failures/payment-service-unavailable) | Yes |

<Info>
  `gateway:request_failed` and `gateway:unavailable` both mean "no session was created, and it's safe to retry the same create call" — they differ in cause (a genuine upstream failure vs. a breaker deliberately short-circuiting). Branch on `code`/`type`, not the HTTP status alone.
</Info>

## Rate limits and server errors

| Code | HTTP | Cause | What to do | Retry? |
| - | - | - | - | - |
| <a id="checkout-rate-limit-exceeded" />`rate_limit:exceeded` | 429 | Too many requests | Wait `details.retry_after` seconds, then retry. The response also carries a `Retry-After` HTTP header with the same value in seconds — use either | Yes, after `details.retry_after` / `Retry-After` |
| <a id="checkout-internal-server-error" />`internal:server_error` | 500 | An unexpected server error | Retry, then [contact support](/resources/support) if it recurs | Yes |

## Next steps

<Columns cols={2}>
  <Card title="Payment outcomes" icon="credit-card" href="/hosted-checkout/errors/payment-outcomes">
    Declines, expiry, and cancellation — not errors on this API.
  </Card>

  <Card title="Redirect and signature errors" icon="link" href="/hosted-checkout/errors/redirect-and-signature-errors">
    Signature verification and return-URL signal reference.
  </Card>

  <Card title="Handle failures" icon="triangle-alert" href="/hosted-checkout/handle-failures/overview">
    Ten common failure scenarios, each with the exact signal and what to do.
  </Card>

  <Card title="Test your integration" icon="flask-conical" href="/resources/test-your-integration#hosted-checkout">
    Exercise these paths in sandbox before you go live.
  </Card>
</Columns>
