> ## 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.

# Service unavailable - Payments API

> When and how to retry safely after a service-unavailable or internal error response.

A call returns `503` or `500` with no business meaning behind it — a dependency was down, or something failed unexpectedly on RadiumOne's side.

<Info>
  **TL;DR** — `503`/`500` on a create-type request behaves like a timeout: retry with the **same** idempotency key, with backoff — never a new one.
</Info>

## When this happens

* `503 urn:radiumone:gateway:service-unavailable` — a downstream service RadiumOne depends on is unreachable.
* `503 urn:radiumone:gateway:vault-unavailable` — the encryption-key service backing card tokenization is unavailable. RadiumOne returns this generic form deliberately, without leaking which key or vault path failed.
* `500 urn:radiumone:gateway:internal-error` — an unexpected failure that isn't one of the above.

## What you see

| Signal | Value |
| - | - |
| HTTP status | `503` or `500` |
| Error type | `gateway:service-unavailable` / `gateway:vault-unavailable` / `gateway:internal-error` |

<Warning>
  These URNs don't all carry an explicit `retry_allowed` extension. **Treat any `503`/`500` on a create-type request the same as a [timeout](/payments-api/handle-failures/timeouts-and-unknown-outcomes)** — retry with the same idempotency key, never a new one, because the request may have partially processed before the failure surfaced.
</Warning>

## What to do

<Steps>
  <Step title="Check for a retry signal first">
    If the error body carries `retry_allowed`, or the response has a `Retry-After` header, honor it directly — see [Problem format and retries: retry rules](/payments-api/errors/problem-format-and-retries#retry-rules).
  </Step>

  <Step title="Otherwise, branch on URN and retry_allowed, never on the HTTP code alone">
    **Any 2xx response is a result you must branch on `status`** — never on `response_code` (that's the verbatim host/acquirer code; useful for support tickets, not for your app logic).

    | Status | Meaning | What to do |
    | - | - | - |
    | `AUTHORIZED` | Funds reserved (authorize only) | Capture within the capture window, or void to release |
    | `CAPTURED` | Funds captured (purchase, capture, or refund) | Fulfil the order (or process the refund) |
    | `VOIDED` | Authorization released | No funds moved |
    | `DECLINED` | Issuer or acquirer declined | Final for this attempt — don't retry the same card without a new attempt from the shopper |
    | `FAILED` | The transaction didn't complete — the acquirer returned a non-decline error code, or the gateway couldn't place the request. **Not a guarantee that no funds moved** — `VOIDED` and `REVERSED` are the only statuses that positively assert that. | Confirm via `GET /v1/transactions/{id}/status` before retrying, then retry (a genuinely new attempt, not a replay of the same `request_id`) with a **new** `request_id` |
    | `PENDING` | Outcome not yet known (async) | Wait for a webhook, or poll `GET /v1/transactions/{id}/status` |
    | `AUTH_EXPIRED` | Authorization lapsed before capture | Create a new authorization |
    | `REVERSAL_PENDING` / `REVERSED` | Automatic compensating reversal after an upstream timeout left the outcome genuinely unknown (never left `FAILED` in this case) | No merchant action; webhook confirms the final state |

    | Key | Used by | On replay |
    | - | - | - |
    | `request_id` | Purchase, authorize, standalone and referenced refunds | Same body, same operation type → the original transaction, whatever its status — including `PENDING`, `DECLINED`, or `FAILED`. Changed body, or the same key reused for a different operation type → [`transaction:idempotency-body-mismatch`](/payments-api/errors/payment-operation-errors#transaction-idempotency-body-mismatch). Purchase/authorize/standalone-refund compare `amount`, `currency`, `payment_method_type`, `channel`, the card's `pan_prefix` (first 8 digits — not the full token), `metadata`, and `order_reference`; a **referenced refund** compares only the original transaction and `amount` (`reason` isn't compared) and its replay check runs before the refund gates, so it always replays, even a `DECLINED`/`FAILED` one — mint a **new** `request_id` to retry after a decline. None of these compare `three_ds` or `loyalty`, so changing either on a retry replays the original silently instead of failing. |
    | `operation_id` | Capture, void | Same operation type on the same transaction → the original result (body is never compared, so a changed amount is silently ignored). A different operation type reusing the key → [`tx:duplicate-operation`](/payments-api/errors/payment-operation-errors#tx-duplicate-operation). |

    <Warning>
      Balance inquiry also takes a `request_id` field, but it isn't an idempotency key — there's no dedup or replay store. Every call re-queries the rewards host, even with the same `request_id`.
    </Warning>

    <Tip>
      Keys are 8–64 characters, `[a-zA-Z0-9-]` only, unique per merchant account. Generate one key per order **attempt** and persist it to your database before you send the first request — never mint a new key just to retry the same attempt. See [Prevent duplicate payments](/get-started/api-basics/prevent-duplicate-payments).
    </Tip>

    `PENDING` means the outcome isn't known yet — most often after a processor timeout. Don't assume success or failure. Recover it one of two ways:

    1. **Wait for a webhook** (`payment.*`, `authorization.*`, `refund.*` — see [Webhook event types](/payments-api/webhooks/event-types)).
    2. **Call `GET /v1/transactions/{id}/status`** for a live inquiry against the acquirer.

    If you don't have the transaction `id` yet — a client-side timeout before the first response arrived — replay the same request with the same `request_id` and body. The replay returns the stored transaction and its `id`, whatever status it's reached. Never re-submit with a **new** idempotency key just because the first attempt is slow — that risks a second charge for the same order.
  </Step>

  <Step title="Retry with backoff, using the same key">
    Retry a purchase/authorize/refund with the same `request_id`, or a capture/void with the same `operation_id`. Add jittered backoff between attempts rather than retrying immediately.
  </Step>

  <Step title="Fall back to timeout handling if retries don't resolve it">
    If the outcome still isn't clear after a retry, follow [Handle timeouts and unknown outcomes](/payments-api/handle-failures/timeouts-and-unknown-outcomes) — wait for the confirming webhook (recommended), or check status now if you don't use webhooks or need an answer sooner, rather than continuing to retry indefinitely.
  </Step>
</Steps>

## Related

<Columns cols={2}>
  <Card title="Problem format and retries" icon="triangle-alert" href="/payments-api/errors/problem-format-and-retries#retry-rules">
    The full retry-rules reference.
  </Card>

  <Card title="Handle timeouts and unknown outcomes" icon="clock-alert" href="/payments-api/handle-failures/timeouts-and-unknown-outcomes">
    The idempotent-retry pattern this page reuses.
  </Card>
</Columns>
