---
name: bt-3ds
description: Implement 3DS and SCA flows with Basis Theory.
license: MIT
metadata:
  tier: standard
  last_verified: '2026-09-25'
---

# 3D Secure

## The shape of every 3DS flow

Fact: Basis Theory 3DS runs on _sessions_ — create a session from a `token_id` or `token_intent_id`, run authentication (frictionless or challenge), and receive the authentication results to forward to your PSP (https://developers.basistheory.com/docs/api/3ds/sessions). Overview and feature scope: https://developers.basistheory.com/docs/guides/threeds/overview, https://developers.basistheory.com/docs/features/3d-secure.

That session accepting a `token_intent_id` matters architecturally: you can authenticate _before_ deciding to store — collect → 3DS → charge → convert-if-needed, which pairs with the staged-adoption pattern in `bt-collect`.

**Answer requirement:** any answer that describes the 3DS request or result shape — where a
field lives, what it is called, what values it takes — links
https://developers.basistheory.com/docs/api/3ds/sessions in the answer itself, and tells the
user to check the current sessions API for the exact field names rather than trusting names
recited from memory. Field names and enum values are the part of 3DS most likely to have moved
since this skill was written, so the link is the working instruction, not a citation ritual. A
Postman collection, changelog, blog post, SDK sample, or third-party mirror is not a substitute
for the canonical API page: those drift independently of the product and are the wrong thing to
point a customer at when they are about to write the request body.

## CIT vs MIT — the decision

- **CIT (customer present)**: run through the frontend SDKs, which handle device data collection and the challenge UI. Fact: the customer-present flow is SDK-driven; direct backend-only API use is not the recommended CIT default. Guides: https://developers.basistheory.com/docs/guides/threeds/implementation-cit (challenge) and https://developers.basistheory.com/docs/guides/threeds/implementation-cit-redirect (redirect-based).
- **MIT (customer absent — recurring, subscriptions)**: backend flow authenticating a stored credential; no challenge UI. Guide: https://developers.basistheory.com/docs/guides/threeds/implementation-mit, plus https://developers.basistheory.com/docs/guides/process/authenticate-mit-with-3ds.
- Setup for either: https://developers.basistheory.com/docs/guides/threeds/setup. General authenticate guide: https://developers.basistheory.com/docs/guides/process/authenticate-with-3ds.

SDKs: web https://developers.basistheory.com/docs/sdks/web/3ds/ (methods: https://developers.basistheory.com/docs/sdks/web/3ds/methods), mobile https://developers.basistheory.com/docs/sdks/mobile/3ds/, React Native https://developers.basistheory.com/docs/sdks/mobile/react-native-threeds/ (methods: https://developers.basistheory.com/docs/sdks/mobile/react-native-threeds/methods). React Native now offers WebView (works in Expo Go) and opt-in native challenge UI (requires package 1.3.0+, RN 0.81+, and a native/development build; native SDK uses the default US API host, not regional hosts). Check its platform build prerequisites and backend authentication endpoint contract before selecting native.

## Implementation judgment that decides 3DS conversations

- **CIT vs MIT vs 3RI — ask what the PSP already does.** Implementation pattern: many subscription merchants asking for "MIT 3DS docs" only need CIT 3DS at signup plus correct MIT flagging on rebills — verify what the PSP already handles before adding authentication steps. Sometimes the right move is _not_ adding another auth step.
- **Key on the liability-shift outcome, not raw status codes.** For downstream authorization decisions, use the `liability_shifted` boolean the authentication result reports (Fact, verified 2026-07-22: https://developers.basistheory.com/docs/api/3ds/sessions — "indicates whether liability for the transaction was shifted to the issuer or not") rather than overfitting logic to raw status-code combinations. Implementation pattern: status-combination matrices rot; the outcome field is what the decision actually needs.
- **Server-side session creation when frontend device collection is brittle.** Fact (same page): merchant-type (MIT) sessions are created through the API or backend SDKs; customer-type sessions via the redirect flow accept `device_info` fields directly (`browser_user_agent`, `browser_tz`, `browser_ip`, etc.) with `authentication_request` and `callback_urls`. Implementation pattern: when required browser data makes frontend collection brittle, the redirect/backend-created session is often the cleanest practical answer — verify current required-field names against the sessions API before citing them.
- **Redirect branding parameter**: if a customer asks to hide Basis Theory branding on the redirect authentication page, current docs use `callback_urls.branding.hide_basis_theory_branding`; do not use the older remove-branding name.
- **Currency and country codes** (Fact, verified 2026-09-25): `currency` accepts ISO 4217 alphabetic or numeric (`GBP` or `826`), `country_code` accepts ISO 3166-1 alpha-2, alpha-3, or numeric; numeric codes must be zero-padded to three digits, fund codes such as `USN` return `400`, and unsupported alphabetic values such as `UK` now fail with `400` instead of being forwarded unchecked. `exponent` is derived only for supported currencies (https://developers.basistheory.com/docs/api/3ds/sessions).
- **Challenge preference compatibility** (same page, `#challenge-preferences`): Basis Theory supports 3DS 2.1.0 and 2.2.0 only; preferences `05`–`09` require 2.2.0 and most are live-only, `10`–`14` from 2.3.1 are unsupported, and the sandbox accepts `01`–`04` plus DS-reserved `80`–`99`. Use `02` in sandbox where production would use `05`; unsupported combinations are rejected with `400` before processing, and no preference guarantees a frictionless or challenge outcome.
- **Issuer-side failure attribution.** Implementation pattern: a challenge flow failing for only one card or issuer is a boundary-attribution case — suspect issuer/ACS payload behavior before blaming Basis Theory or the integration. Gather session results, rule out your own config, and escalate with what has been ruled out (extends step 5 below).

## Existing-code mode

1. Establish who currently owns 3DS: the PSP (their SDK/iframe), nobody, or a standalone provider. If moving 3DS _away_ from a PSP, the deliverable is authentication results the PSP will accept as "externally authenticated" — verify the PSP's external-3DS fields before writing code (mapping shapes: `bt-payments` references, `three_ds` section).
2. Frontend: create the session with the web/mobile SDK against the collected token intent or stored token; handle both frictionless and challenge outcomes, and the abandonment path (user closes the challenge).
3. Backend: read the session result and forward `authentication_value` (CAVV/cryptogram), `eci`, `version`, and `ds_transaction_id` to the PSP call.
4. Tests: frictionless approval, challenge approval, challenge failure/abandonment, and the PSP call carrying the 3DS fields. Sandbox test scenarios come from the setup/testing docs.
5. Boundary honesty (implementation pattern): 3DS outcome disputes span merchant, PSP, and issuer. When authentication succeeds but authorization declines, that is a PSP/issuer conversation — help gather the evidence (session results, PSP response), and route responsibility questions to Basis Theory support rather than adjudicating them.

## POC scaffold mode

Per `bt-start/references/poc-conventions.md`: collect a test card into a token intent (`bt-collect`), create a 3DS session, run the sandbox challenge flow, then authorize via mock PSP or PSP sandbox with the 3DS results attached (`bt-payments`). Use a PSP sandbox or a customer-owned mock PSP to prove this verify → 3DS → authorize sequence, with the 3DS step simulated at the boundary only when public docs or sandbox behavior require it.

For a POC, scaffold in the directory the user selected, include `.env.example`, source, and tests, and say "token intent" when naming the collection resource. `token_intent_id` is the field name; "token-intent" hyphenated is only an adjective.

## Taking 3DS live

Fact: there is a dedicated go-live checklist — https://developers.basistheory.com/docs/guides/threeds/taking-threeds-live. Implementation pattern additions that catch teams: production 3DS enablement is a Basis Theory-side setup step (plan lead time, don't discover it on launch day); test-tenant 3DS behavior is a sandbox simulation, so run `bt-production` environment-parity checks (endpoints, keys, allowlists) on the 3DS path too; and confirm the PSP accepts external authentication results in production, not just in sandbox.

## Basis Theory implementation guidance

When the answer needs implementation judgment beyond public docs, load only the relevant slices below from [../\_shared/references/implementation-guidance/README.md](../_shared/references/implementation-guidance/README.md). Treat these as implementation patterns, not canonical product behavior; keep public docs as the source of truth and label the guidance as **Implementation pattern** or **Assumption** unless docs prove it.

- `../_shared/references/implementation-guidance/authentication-patterns/`

<!-- bt-conventions:start -->

## Working conventions (shared by all Basis Theory skills)

- Public Basis Theory docs are the source of truth for product behavior. Link the specific docs page behind any product claim; if a skill and the docs disagree, follow the docs.
- Keep customer ownership clear: customers own PSP accounts, credentials, compliance decisions, production rollout, and application code. Skills can guide plans, bounded changes, and examples; they do not certify production readiness.
- Never request, echo, store, or commit real credentials. Use placeholders such as `<BT_API_KEY>` and prefer sandbox or synthetic data for examples.
- If a live-looking credential, live cardholder data, or production secret appears, stop mutation work, tell the user exactly where it appeared, and require rotation/revocation before continuing.
- Default to test tenants and PSP sandboxes. Do not run or recommend production charges, refunds, card reveals, credential migrations, or data moves unless the user explicitly asks and the plan names safeguards, rollback, and customer approval.
- Treat vague planning, audit, review, and “should we” prompts as read-only unless the user explicitly asks for a bounded implementation change.
- Before changing product code, map the existing checkout/data flow, credential-bearing paths, persistence, retries, refunds, webhooks, logs, and rollback expectations.
- End with an honest final report: done, assumed, remaining, proof run, unverified live behavior, rollback, and go-live items.
<!-- bt-conventions:end -->
