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

# Digital Asset Accounts iOS SDK

> Reference for the iOS SDK that runs passkey approvals in a native application and holds the authorization key for the React Native SDK

> [!NOTE]
>
> The iOS SDK is available in sandbox only. Production support is on the roadmap.

The iOS SDK runs in your application on the end customer's iPhone. It never
holds your API key and makes no calls to the Circle API itself. Your backend
does that and passes your application only the short-lived token for one
ceremony.

## What you can do

* **Approve operations with a passkey**: run passkey enrollment and approval for
  [Strong Customer Authentication (SCA)](https://developers.circle.com/digital-asset-accounts/howtos/strong-customer-authentication.md)
  in a Swift application.
* **Hold the authorization key for non-custodial accounts**: the
  [React Native SDK](https://developers.circle.com/sdks/daa/react-native-sdk.md) calls this SDK to create the
  end customer's authorization key in the Secure Enclave and sign with it. You
  don't call these methods yourself. To build a
  [non-custodial account](https://developers.circle.com/digital-asset-accounts/concepts/non-custodial-accounts.md)
  integration, use the React Native SDK.

## Requirements

| Requirement | Minimum |
| - | - |
| Swift | 5.9 |
| Xcode | 15.3 |
| iOS, to install the SDK and hold the authorization key | 15.0 |
| iOS, for passkey enrollment and approval | 17.4 |

Passkey enrollment and approval need iOS 17.4 or later, because Circle accepts
only `https` callback URLs, which `ASWebAuthenticationSession` supports from iOS
17.4. On earlier versions, or in an app built with Xcode earlier than 15.3,
`DaaScaConfiguration` throws
`DaaScaConfigurationError.httpsCallbackUnavailable`. If you serve end customers
who need Strong Customer Authentication, check for this error and tell the end
customer to update iOS before they can approve an operation.

## Install

> [tab] Swift Package Manager
>
> Add the package to your `Package.swift`, or add it in Xcode under **File > Add
> Package Dependencies**:
>
> ```swift theme={null}
> dependencies: [
>     .package(url: "https://github.com/circlefin/daa-ios-sdk.git", exact: "0.7.1"),
> ]
> ```
>
> Then add the `DaaSdk` product to your application target.

> [tab] CocoaPods
>
> Add Circle's spec repository to your `Podfile` before the CocoaPods CDN source,
> then add the pod:
>
> ```ruby theme={null}
> source "https://github.com/circlefin/daa-ios-sdk.git"
> source "https://cdn.cocoapods.org/"
>
> pod "DaaSdk", "0.7.1"
> ```
>
> ```bash theme={null}
> pod install
> ```

## Set up the callback

When a ceremony finishes, Circle's page returns the result to your application
through an app callback URI. Set it up once for each environment:

1. Choose an `https` callback URL on a domain that you control, such as
   `https://app.example.com/sca/callback`. The SDK refuses a URL that includes a
   port or an IP address. Circle doesn't accept custom schemes.
2. Register every exact callback URL that you use in Circle Hub, under
   **Settings > Digital Asset Accounts > SCA**. Register separately in
   [sandbox](https://hub-sandbox.circle.com) and
   [production](https://hub.circle.com).
3. Add a `webcredentials:` entry for the host to your application's Associated
   Domains, and serve the matching `apple-app-site-association` file.

Your backend sends the same callback URL as `redirectUri` when it opens each
passkey registration or challenge. It replaces `embedOrigin`, which applies only
to web pages. If the value doesn't exactly match a registered URL, Circle
returns error code 420068.

## Initialize

Create a `DaaSca` with a `DaaScaConfiguration`:

```swift theme={null}
import DaaSdk

let sca = DaaSca(configuration: try DaaScaConfiguration(
    environment: .sandbox,
    redirectURI: "https://app.example.com/sca/callback"
))
```

| Parameter | Required | Description |
| - | - | - |
| `environment` | Yes | `.sandbox` or `.production` |
| `redirectURI` | Yes | Your registered callback URL, exactly as registered. Up to 2,048 characters |
| `approveTimeout` | No | How long an approval can take, in seconds. Defaults to 300. Must be from 30 to 3,600 |
| `enrollTimeout` | No | How long an enrollment can take, in seconds. Defaults to 600. Must be from 30 to 3,600 |

An invalid value throws `DaaScaConfigurationError`.

## Methods

`DaaSca` runs on the main actor. Its ceremony methods are `async` and present
Circle's page in an `ASWebAuthenticationSession` sheet.

| Method | Returns | Description |
| - | - | - |
| `enroll(frameToken:)` | `String` | Runs passkey enrollment. Send the returned registration response to your backend unchanged |
| `approve(frameToken:)` | `String` | Runs an approval. Your backend sends the returned value, unchanged, in the `X-Sca-Assertion` header |
| `enroll(frameToken:presentationAnchor:)` | `String` | Same as `enroll(frameToken:)`, presented over the window that you pass |
| `approve(frameToken:presentationAnchor:)` | `String` | Same as `approve(frameToken:)`, presented over the window that you pass |
| `capabilities()` | `DaaScaCapabilities` | Reports whether this device can run a ceremony. `isCeremonySupported` is `true` when the sheet, callback, and passkeys are available |

Without an anchor, the SDK presents over the key window of the foreground scene.
Pass an anchor when your application has more than one window, such as on iPad.
One ceremony runs at a time: starting another one, or cancelling the Swift
`Task`, cancels the open ceremony.

This example approves a transfer. Your backend opens the challenge, returns only
the frame token, and later sends the transfer with the assertion:

```swift theme={null}
func approveTransfer(_ transferId: String) async {
    do {
        let frameToken = try await backend.startTransfer(transferId)
        let assertion = try await sca.approve(frameToken: frameToken)
        // Send the assertion exactly as returned. Don't parse or re-encode it.
        try await backend.confirmTransfer(transferId, assertion: assertion)
    } catch DaaScaError.cancelled {
        // The end customer closed the sheet. Let them try again.
    } catch {
        // See Errors.
    }
}
```

For the backend steps, see
[How-to: Implement Strong Customer Authentication](https://developers.circle.com/digital-asset-accounts/howtos/strong-customer-authentication.md).

## Errors

The ceremony methods throw `DaaScaError`:

| Case | Cause | What to do |
| - | - | - |
| `cancelled` | The end customer closed the sheet, or another ceremony replaced this one | Let them try again |
| `expired(message:)` | The frame token or the ceremony timed out | Ask your backend for a new frame token |
| `alreadyEnrolled(message:)` | The end customer already has a passkey on this device | Skip enrollment |
| `unsupported(message:)` | The device can't run the ceremony, for example because passkeys aren't available | Check `capabilities()` |
| `sessionUnavailable(message:)` | The sheet couldn't be shown: there's no window to present over, or this iOS version can't deliver the callback | Check your callback setup, or pass a `presentationAnchor` |
| `stateMismatch` | The callback didn't belong to the open ceremony | An integration bug. Don't retry automatically |
| `` `protocol`(message:) `` | The ceremony returned an unexpected result, such as an invalid frame token | An integration bug. Don't offer a different authentication method instead |

`message` is for your logs. Don't show it to the end customer.

## Versions

The SDK follows semantic versioning. Before version 1.0.0, a minor version can
include breaking changes, so pin an exact version. If you also use the React
Native SDK, use the iOS SDK version that it pins.
