Skip to main content
Check whether an end user requires SCA, enroll a passkey for them, then obtain a signed approval for each sensitive operation and submit it alongside the API request. For what an approval proves and why the ceremony runs in a Circle-hosted iframe, see How Strong Customer Authentication works.
Implement SCA if you serve end users in the European Economic Area (EEA). Whether SCA applies depends on each end user, not on your platform. Check each end user, as described in step 1.
The SCA ceremony runs in the browser. There is no iOS, Android, or React Native SDK for it yet. Native applications must host the ceremony in a web view or the system browser.

Prerequisites

Before you begin, ensure that you’ve:
  • Registered your application origin with Circle. Reach out to your support team with the exact origin (scheme + host, for example https://app.example.com). Registration is required separately for sandbox and production.
  • Installed the DAA web SDK.
  • Obtained a clientEntityId for the end user you’re enrolling. See Onboard customers for how to create one.
  • Run a device check for the end user and stored the deviceId it returned. See Collect device risk signals. Every protected operation sends that deviceId in riskSignals.
Reuse the deviceId returned by checkDevice(). Do not generate one. Circle resolves it against the completed device check, and an unrecognized value is accepted and then declined: the endpoint returns 201 and the transaction later settles as failed.
The Digital Asset Accounts API base URL is https://api-sandbox.circle.com for sandbox and https://api.circle.com for production. Set your API key in the Authorization header using the format Bearer YOUR_API_KEY. See Sandbox environment and Going to production for environment details.

Steps

Step 1. Check whether the end user requires SCA

List the end user’s passkeys with GET /v1/accounts/passkeys. The response carries both the SCA requirement and the passkeys already enrolled:
Response
The list includes revoked passkeys. An end user has an active passkey when at least one entry has revokedDate set to null. Use sca.required to decide what to do before a protected operation: If sca is null, don’t store it. Call the endpoint as usual and handle HTTP 428 if it’s returned. Handle a 428 even when sca.required is false. For what each value means, whether you can store it, and how voluntary SCA works, see Which end users require SCA.

Step 2. Enroll a passkey

Enroll a passkey for each end user who requires SCA, before they run any protected operation. Run this flow once per end user, and again if they revoke their last active passkey or can’t access it on a new device.
If your application origin isn’t registered, the request to create a registration session fails with HTTP 400 and error code 420064. If your page runs on an origin other than the one you registered, such as localhost, the ceremony fails. Register each origin, for sandbox and production, before running this flow.

Step 2.1. Create a registration session

Response
The frame token expires after 10 minutes. Pass it to your frontend right away. Do not re-send the request with the same idempotencyKey after delivering the token. This rotates the token and invalidates the copy you already sent. Start a fresh session with a new idempotencyKey if the token expires.

Step 2.2. Run the enrollment ceremony

Initialize the SDK on your frontend and call sca.enroll with the frame token from step 2.1.
The SDK renders the passkey prompt in a Circle-hosted iframe. Pass attestationResponse to your backend as-is. Re-serializing it causes a verification error.

Step 2.3. Complete the registration

Send the attestationResponse from the SDK to your backend. Then forward it to Circle to complete enrollment.
Response

Step 3. Approve a protected operation

For every protected operation, obtain a signed challenge and include it in the API request.

Step 3.1. Create a challenge

Response
The summary is a server-generated record of the operation. Don’t use it to render a pre-confirmation to the end user, because the Circle iframe shows its own description to the user.
The frame token expires after 5 minutes. Pass it to your frontend right away. Re-sending with the same idempotencyKey rotates the token and invalidates the copy you already sent. Create a new challenge with a fresh idempotencyKey if the token expires.
The intent must be identical to the operation request body: same fields, same values. An extra or missing field causes an intent mismatch error (420047). For the full list of intent shapes by operation, see the Create a challenge API reference. Set operation to match the endpoint you are about to call: TRANSFER, WITHDRAWAL, ADDRESS_BOOK_ADD, ADDRESS_BOOK_DELETE, or WIRE_ACCOUNT_CREATE. ADDRESS_BOOK_DELETE is the one exception. That endpoint takes no request body, so send an empty intent and put the address ID in pathParameters:
The idempotencyKey in the intent must match the idempotencyKey in the operation request body. A mismatch causes an intent mismatch error.

Step 3.2. Run the approval ceremony

Pass the frameToken from the challenge response to the SDK’s approve method. The SDK renders the passkey prompt and returns a signed assertion string.

Step 3.3. Submit the protected operation

Include the challengeId and assertion as request headers. Use the same field values you set in the challenge intent.
Response
201 means the operation was accepted, not that it succeeded. A transfer is screened after it is created and can settle as failed. Poll GET /v1/accounts/transfers/{id} until status is terminal, and read errorCode and riskEvaluation on that response to see why, for example errorCode: transfer_denied with riskEvaluation.decision: denied. The list endpoint omits riskEvaluation, so fetch the transfer by id. A declined device check or an unrecognized deviceId surfaces here, not on the create call.
If the request was rejected before the transfer was created, the endpoint returns an error instead of 201.
If a protected endpoint returns HTTP 428 (error code 420058), the X-Sca-Challenge-Id or X-Sca-Assertion header was omitted from the request. Circle requires SCA for this end user. If they have no active passkey, enroll one (step 2) first. Then run the approval ceremony and retry with both headers present. For other SCA errors, see the Digital Asset Accounts API reference.