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

# 3DS authentication results - Elements SDK

> Every 3D Secure status, ECI meaning and error code, with the UX and server action for each.

This page is the single reference for 3D Secure statuses, ECI values and error
codes across [3DS with Elements](/elements/three-d-secure/add-three-d-secure),
[Challenge presentation](/elements/three-d-secure/challenge-presentation) and
[Your own 3DS provider](/payments-api/three-d-secure/use-your-own-provider). For
the recovery-action version of the same errors, see
[Handle 3D Secure failures in Elements](/elements/handle-failures/three-ds-failures).

## Status meanings

`threeDS.authenticate()` and `threeDS.resume()` resolve with `{ status, ref }`.
**A decline resolves — it never throws.** Use the status only to drive your
UX; your server action is the same regardless (see
[Server policy](/get-started/three-d-secure#server-policy)).

| Status | What it means | Your UX | Your server action |
| - | - | - | - |
| `AUTHENTICATED` | Issuer confirmed the shopper | Proceed to payment | Purchase/authorize with `three_ds:{ref}` — the gateway independently verifies the ref before it authorizes |
| `ATTEMPTED` | Issuer or scheme attempted authentication (usually still shifts liability) | Proceed to payment | Same as above |
| `NOT_AUTHENTICATED` | Issuer explicitly did not authenticate the shopper | Your risk policy decides whether to still offer payment | Same as above — the gateway decides whether it accepts this ref for the charge |
| `FAILED` | Authentication couldn't complete (technical failure) | Show a generic error; let the shopper retry the payment from scratch | Don't submit this ref |
| `REJECTED` | Issuer rejected the authentication | Show a decline; don't automatically retry the same card | Don't submit this ref |
| `EXPIRED` | The authentication window lapsed | Restart the checkout | Don't submit this ref |

`PENDING` and `DECOUPLED` are intermediate states the SDK handles internally
(polling on your behalf) and are never returned as a final result — see
[Decoupled authentication](/elements/three-d-secure/challenge-presentation#decoupled-authentication).

<Warning>
  Whatever the status, your server must still independently verify the ref at
  charge time. Never skip that check because the client reported
  `AUTHENTICATED` — see
  [Server policy](/get-started/three-d-secure#server-policy).
</Warning>

## ECI meanings

The electronic commerce indicator (ECI) records how strongly a card scheme
backs an authentication, and whether liability shifts to the issuer. RadiumOne's
own 3DS flow doesn't currently return the ECI to your server.

If you use
[your own 3DS provider](/payments-api/three-d-secure/use-your-own-provider), you submit the ECI
yourself; see the [full ECI table](/payments-api/three-d-secure/use-your-own-provider#eci-values)
there.

## SDK errors

These are thrown by `threeDS.authenticate()`/`resume()` in the browser, before
your server is ever involved:

| Code | Retry? | Remedy |
| - | - | - |
| `urn:radiumone:three-ds:provider-unavailable` | No | Show a retry affordance; don't authorize |
| `urn:radiumone:three-ds:challenge-timeout` | Yes | Check your CSP's `connect-src`/`frame-src` first |
| `urn:radiumone:three-ds:action-not-found` | No | Restart the payment |
| `urn:radiumone:three-ds:cancelled` | Yes | Shopper cancelled — return to the payment form |
| `urn:radiumone:three-ds:challenge-display-unsupported` | No | Pass a `returnUrl` or use `challengePresentation: "auto"` |
| `urn:radiumone:three-ds:render-mode-unsupported` | No | Update the Elements SDK |
| `urn:radiumone:three-ds:unsupported-action` | No | Update the Elements SDK |
| `urn:radiumone:three-ds:action-malformed` | No | Report to support |

## Gateway errors

These come from the gateway, either while authenticating (`authenticate()`/
`resume()` calls) or at the purchase/authorize call itself:

| Code | HTTP | When | Remedy |
| - | - | - | - |
| `urn:radiumone:three-ds:session-not-bound` | 422 | `authenticate()` called before `elements.submit()` | Tokenize the card first |
| `urn:radiumone:three-ds:session-not-amount-bound` | 422 | The session's amount wasn't pinned before authentication | `PATCH /v1/sessions/{id}` with `net_payable_amount` before `authenticate()` |
| `urn:radiumone:three-ds:session-not-found` | 404 | The session expired or is unknown | Restart the checkout with a new session |
| `urn:radiumone:three-ds:card-token-invalid` | 422 | The bind token lapsed (about 30 minutes) or changed | Re-bind the card (`elements.submit()`) and re-authenticate |
| `urn:radiumone:three-ds:session-attempts-exceeded` | 429 | Too many authentication attempts on this session | Start a new session — don't retry the same one |
| `urn:radiumone:rate-limit-exceeded` | 429 | Too many requests | Retry after the `Retry-After`/`retryAfter` value |
| `urn:radiumone:three-ds:card-token-mismatch` | 422 | At charge time: the token you're charging differs from the one that was authenticated | Re-bind and re-authenticate before charging |
| `urn:radiumone:three-ds:ref-invalid` | 422 | The `ref` is unknown, already used, or doesn't match this charge | Re-authenticate; don't reuse a ref across charges |
| `urn:radiumone:three-ds:ref-expired` | 422 | The `ref` is too old to use | Re-authenticate |
| `urn:radiumone:three-ds:amount-exceeds-authenticated` | 422 | The charge amount exceeds what was authenticated | Charge the authenticated (net payable) amount, or re-authenticate for the new amount |
| `urn:radiumone:three-ds:not-authenticated` | 403 | The charge was attempted with a failed or missing authentication | Don't submit this ref — the shopper needs to re-authenticate |
| `urn:radiumone:three-ds:authentication-required` | 422 | An acquirer mandate requires 3DS and none was provided | Run 3DS for this charge (Elements or your own provider) |
| `urn:radiumone:three-ds:external-auth-not-permitted` | 422 | Own-provider evidence submitted without outlet enablement | [Request enablement](/resources/support#request-enablement) |
| `urn:radiumone:three-ds:provider-unavailable` / `not-configured` | 503 | The 3DS provider is unreachable or misconfigured | Retry later; don't authorize |
| `urn:radiumone:gateway:validation-error` | 400 | A `three_ds` field is malformed or an unrecognized key was sent | Check field limits for the arm you're using |

<Danger>
  Never use a secret key (`r1sk_…`) in browser code, mobile apps, or anywhere a shopper can inspect it. Secret keys belong on your server only.
</Danger>

## Next steps

<Columns cols={2}>
  <Card title="3DS with Elements" icon="credit-card" href="/elements/three-d-secure/add-three-d-secure">
    Where these statuses and errors come from in the authenticate/charge flow.
  </Card>

  <Card title="Use your own 3DS provider" icon="shield-check" href="/payments-api/three-d-secure/use-your-own-provider">
    ECI values you submit yourself on this path.
  </Card>
</Columns>
