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

> ## Agent Instructions
> Before reading these docs, install the relevant Circle Skill to get pre-loaded context — skills are LLM-optimized and cover architecture decisions, correct flows, and common pitfalls. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills. Available skills: use-usdc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md (balances, transfers, approvals on EVM and Solana); EURC has no skill — see https://developers.circle.com/stablecoins/what-is-eurc.md; bridge-stablecoin https://github.com/circlefin/skills/blob/master/plugins/circle/skills/bridge-stablecoin/SKILL.md (crosschain transfers via CCTP, USDC, EURC, and third-party assets); use-arc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md (Arc chain config, contract deployment, USDC bridging — Arc docs: https://docs.arc.io/llms.txt); use-circle-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md (choose wallet type); use-developer-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md (custodial wallets for payouts and treasury); use-user-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md (embedded wallets with Web2 login); use-modular-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md (smart contract wallets with passkeys and ERC-4337); use-gateway https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md (unified USDC balance, nanopayments); use-smart-contract-platform https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md (deploy, interact, monitor contracts). Pair any skill with the Circle MCP server for live SDK signatures, contract addresses, and chain IDs: {"mcpServers":{"circle":{"url":"https://api.circle.com/v1/codegen/mcp"}}}.
> Read the product overview first. Each product has an overview explaining when and why to use it. Read it before quickstarts.
> Pick the right wallet type. Developer-controlled (you hold keys), user-controlled (end user holds keys via PIN/social login), or modular (smart contract wallets with passkeys). See https://developers.circle.com/wallets.md.
> Use CCTP V2 for crosschain transfers. CCTP natively transfers USDC and EURC via burn-and-mint, and supports permissionless wrapping of third-party assets via lock-and-mint. V1 is legacy; always use V2 unless V1 is specified or the chain requires it (Noble, Sui, and Aptos still require V1). Migration guide: https://developers.circle.com/cctp/migration-from-v1-to-v2.md.
> Use Bridge Kit for frontend bridging. Wraps CCTP with a simpler SDK for user-facing apps. Use CCTP directly for backend transfers.
> Gas Station vs Paymaster. Gas Station sponsors gas for Circle Wallet transactions. Paymaster lets users pay gas in USDC. Different use cases — read both overviews.
> Gateway Nanopayments for sub-cent payments. Gasless USDC micropayments down to $0.000001 via x402 and batched settlement. For pay-per-request APIs, AI agent payments, streaming.
> Look up USDC addresses per chain. Never hardcode — use https://developers.circle.com/stablecoins/usdc-contract-addresses.md.
> Prefer SDKs over raw API calls. Node.js and Python SDKs handle auth, retries, and errors.
> API key required. Bearer token in Authorization header. Testnet and mainnet use separate keys and may use different base URLs depending on the product.
> Set up webhooks when available. Most operations are async. Webhooks deliver transaction confirmations and state changes.
> When calling list endpoints, paginate using pageSize and pageAfter until no nextPageAfter cursor is returned—stopping at the first page silently misses records.
> Building an AI agent? Start with the Agent Stack—Circle CLI, agent wallets, and nanopayments built for autonomous use cases: https://developers.circle.com/agent-stack.md.

# How Strong Customer Authentication works

> Why Digital Asset Accounts asks end users to approve sensitive operations with a passkey, what an approval proves, and where the trust boundary sits.

Strong Customer Authentication (SCA) makes the end user, not your platform, the
party who authorizes a sensitive operation. The end user approves with a
WebAuthn passkey. Each approval binds to one operation: this amount, this
destination, this account. It works for nothing else. SCA applies to end users
onboarded under Circle's EEA-regulated entity. Scope follows each end user, not
your platform. See [Which end users require SCA](#which-end-users-require-sca).

To implement the flow, see
[How-to: Implement Strong Customer Authentication](/digital-asset-accounts/howtos/strong-customer-authentication).

## Which end users require SCA

Circle determines whether SCA applies to each end user, and reports it as
`sca.required` on
[`GET /v1/accounts/passkeys`](/api-reference/digital-asset-accounts/all/list-passkeys):

* `true`: protected operations for this end user return HTTP 428 without SCA
  headers.
* `false`: Circle doesn't require SCA for this end user.
* `null`: Circle couldn't determine the requirement, for example during a
  temporary outage. It isn't the same as `false`, and it isn't a lasting answer.

A `true` or `false` answer is safe to store. The protected endpoint returns HTTP
428 whenever SCA is required, so an out-of-date answer can't let an operation
through without SCA.

SCA isn't limited to end users who require it. A platform can apply one approval
flow to every end user, with the same flow and headers. Circle verifies the
headers whenever a request carries them, and refuses an invalid, expired, or
mismatched approval, or a request with only one of the two headers, with the
same error an end user who requires SCA would get.

First-party requests that carry no end user, such as adding or removing a
recipient address your platform owns, or linking your platform's own wire bank
account, never require SCA.

## Key terms

| Term | Description |
| - | - |
| Passkey | A WebAuthn credential on the end user's device, unlocked by a biometric sensor, Face ID, Windows Hello, or a security key. |
| Ceremony | The browser interaction where the end user unlocks their passkey. It runs in a Circle-hosted iframe that your frontend embeds. |
| Origin | Your app's domain, meaning scheme plus host. Circle accepts a ceremony only from an origin you registered in advance. |
| Registration session | A short-lived object that coordinates passkey enrollment. |
| Challenge | A short-lived, single-use object that captures the intent of one operation. |
| Intent | The operation body recorded in the challenge. It must match the API request exactly. |
| Assertion | The signed result of an approval ceremony. It proves the end user unlocked their passkey against one specific challenge. |
| Frame token | An opaque token that authorizes one ceremony in the Circle-hosted iframe. Your backend receives it and hands it to your frontend. |

## What an approval proves

A passkey prompt on its own proves only that someone unlocked a device. SCA
proves more than that, because the challenge records the operation before the
end user sees it.

Your backend sends the exact operation body to Circle as the challenge `intent`.
Circle stores it and returns a challenge. The ceremony signs that challenge, and
Circle accepts the resulting assertion only with the matching request. A valid
assertion therefore establishes three things at once:

* **Who approved.** The end user holds the passkey enrolled for their
  `clientEntityId`.
* **What they approved.** The request body Circle verifies is the same body it
  recorded in the intent. Changing the amount, the destination, or any other
  field invalidates the approval and returns error code 420047.
* **That the approval is fresh.** A challenge is single-use and expires in
  minutes, so an intercepted assertion cannot be replayed later or applied to a
  second operation.

The second point is the property that matters most. Without intent binding, a
compromised or buggy frontend could show the end user one transfer and submit
another. Intent binding removes that gap: the operation Circle processes is the
operation the end user saw.

## Trust boundary

Circle is the WebAuthn relying party, not your platform. The end user registers
the passkey against Circle, the ceremony runs in a Circle-hosted iframe, and
Circle verifies the assertion. Your app embeds the iframe and moves opaque
tokens between your frontend and your backend.

```mermaid theme={null}
flowchart LR
    subgraph device["End user's device"]
        PK["Passkey<br/>private key never leaves the device"]
    end
    subgraph app["Your application"]
        APP["Frontend and backend<br/>relay opaque tokens only"]
    end
    subgraph circleZone["Circle"]
        IFRAME["Ceremony iframe<br/>displays the operation<br/>the end user approves"]
        API["Circle API<br/>issues and verifies<br/>the challenge"]
    end
    PK --- IFRAME
    APP --- IFRAME
    APP --- API
    IFRAME --- API
```

Two consequences follow from this split:

* **Your app never renders the approval text.** The iframe fetches the operation
  description from Circle and displays it. The `summary` field on the challenge
  response is a record for your logs, not a source for a confirmation screen you
  build yourself.
* **Your origin must be registered.** Circle runs a ceremony only for an origin
  you registered with your support team. Register sandbox and production
  separately. This stops an unrelated site from driving a ceremony against your
  end users.

## The two ceremonies

| Ceremony | How often | Produces | Consumed by |
| - | - | - | - |
| Passkey enrollment | Once per end user | Attestation response | `POST /v1/accounts/passkeys` |
| Operation approval | Once per protected operation | Assertion | The protected endpoint, as a header pair |

Enrollment must complete before an end user who requires SCA attempts any
protected operation. You choose when to prompt for it, for example during
onboarding or in account settings. Enrolling before the first protected
operation means that operation needs only one ceremony, not two.

## Object lifetimes

Every object in the flow is short-lived, which limits the window in which an
intercepted token is useful.

| Object | Lifetime | Reuse |
| - | - | - |
| Registration session and its token | 10 minutes | One enrollment ceremony |
| Challenge and its token | 5 minutes | One approval ceremony |
| Assertion | Tied to its challenge | Never, single operation only |
| Passkey | Until the end user revokes it | Every subsequent approval |

## Passkeys across devices

A passkey may sync across the end user's devices through iCloud Keychain, Google
Password Manager, or a similar service, depending on their platform. A passkey
enrolled on a phone may therefore be available on a laptop without re-enrolling.

The reverse also holds. An end user on a device with no synced passkey cannot
approve anything until they enroll again. Treat enrollment as a state you check,
not a one-time setup step you assume succeeded.

## Protected operations

| Operation | Endpoint | What the end user approves |
| - | - | - |
| `TRANSFER` | [`POST /v1/accounts/transfers`](/api-reference/digital-asset-accounts/all/create-account-transfer) | Crypto or account-to-account transfer |
| `WITHDRAWAL` | [`POST /v1/accounts/withdrawals`](/api-reference/digital-asset-accounts/all/create-account-withdrawal) | Wire withdrawal |
| `ADDRESS_BOOK_ADD` | [`POST /v1/addresses/recipient`](/api-reference/digital-asset-accounts/all/create-recipient-address) | New trusted blockchain address |
| `ADDRESS_BOOK_DELETE` | [`DELETE /v1/addresses/recipient/{id}`](/api-reference/digital-asset-accounts/all/delete-recipient-address) | Trusted address removal |
| `WIRE_ACCOUNT_CREATE` | [`POST /v1/banks/wires`](/api-reference/digital-asset-accounts/all/create-wire-account) | Wire bank account link |

The list covers two categories: operations that move value, and operations that
change which destinations are trusted. Circle gates address book and bank
account changes too. Otherwise one approval would leave every later transfer to
that destination unattended.

## What SCA does not cover

SCA answers whether the end user approved an operation. It does not answer
whether Circle should process it.

* **Risk screening and limits still apply.** A protected endpoint can accept a
  valid assertion, return `201`, and the operation can still settle as `failed`
  after screening. See
  [Limits and risk ratings](/digital-asset-accounts/concepts/limits-and-risk-ratings)
  and
  [Transaction states](/digital-asset-accounts/references/transaction-states).
* **SCA is not your app's login.** You remain responsible for authenticating the
  end user in your own application and for calling Circle on behalf of the right
  `clientEntityId`.
* **SCA is separate from how you authenticate to Circle.** SCA verifies the end
  user. Your platform authenticates to Circle at the entity level with your API
  key and, for EU/EEA entities under MiCA, mutual TLS (mTLS). mTLS is enforced
  against your entity's API traffic as the distributor, not per end user or per
  `clientEntityId`. See
  [How mTLS authentication works](/circle-mint/mtls-authentication).
* **SCA does not cover read operations.** Only the operations listed in
  [Protected operations](#protected-operations) require an assertion.
