> ## 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-to: Manage recipient addresses

> Register, verify, list, and delete the external blockchain addresses that Digital Asset Accounts send funds to

A recipient address is an external blockchain address that a Digital Asset
Account can send funds to. Register each address before you send to it. Circle
screens the address, and you can send to it only after its `status` is `active`
or `verification_succeeded`. To send funds, see
[Send crypto transfers](https://developers.circle.com/digital-asset-accounts/howtos/crypto-transfers.md).

Your backend manages recipient addresses with your API key, for custodial and
non-custodial accounts alike. For a non-custodial account, register the address
for the same `clientEntityId` as the account. Registration doesn't need the end
customer's signature.

## Prerequisites

Before you begin, ensure that you've:

* Obtained an API key for Digital Asset Accounts from Circle.
* Onboarded the end customer and kept their `clientEntityId`. See
  [Onboard customers](https://developers.circle.com/digital-asset-accounts/quickstarts/onboard-customers.md).
* Run a device check for the end customer and kept the `deviceId` it returns.
  Every request that registers an address carries it in `riskSignals`. See
  [Collect device risk signals](https://developers.circle.com/end-user-onboarding/howtos/collect-device-risk-signals.md).
* For end customers in the European Economic Area (EEA), prepared a Strong
  Customer Authentication (SCA) assertion. Registering a recipient address is an
  SCA-gated operation. See
  [Implement SCA](https://developers.circle.com/digital-asset-accounts/howtos/strong-customer-authentication.md).
* Installed cURL on your development machine.

## Register a recipient address

Register the external blockchain address where you want to send stablecoins. For
every field, see
[`POST /v1/addresses/recipient`](https://developers.circle.com/api-reference/digital-asset-accounts/all/create-recipient-address).

```shell theme={null}
curl --request POST \
  --url https://api-sandbox.circle.com/v1/addresses/recipient \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '
{
  "idempotencyKey": "'"$(uuidgen)"'",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "chain": "ETH",
  "description": "Treasury wallet",
  "clientEntityId": "{clientEntityId}",
  "riskSignals": {
    "ipAddress": "{endUserIp}",
    "sessionId": "{sessionId}",
    "deviceId": "{deviceId}"
  }
}
'
```

**Response**

```json theme={null}
{
  "data": {
    "id": "c7d8e9f0-a1b2-3456-7890-abcdef123456",
    "address": "0x1234567890abcdef1234567890abcdef12345678",
    "chain": "ETH",
    "currency": "USD",
    "description": "Treasury wallet",
    "status": "active"
  }
}
```

Send to the address only when its `status` is `active` or
`verification_succeeded`. If it isn't yet, get it with
`GET /v1/addresses/recipient/{recipientAddressId}` until it is. For end
customers booked to Circle's EEA entity, the request must also declare wallet
`ownership`, and you must send the `X-Sca-Challenge-Id` and `X-Sca-Assertion`
headers from an `ADDRESS_BOOK_ADD` challenge. For the `ownership` object, see
the example in [Register a self-hosted wallet](#register-a-self-hosted-wallet)
and
[EEA API behavior](https://developers.circle.com/digital-asset-accounts/references/eea-api-behavior.md#platform-address-book).

## Register a self-hosted wallet

A self-hosted wallet is a blockchain wallet that an end customer controls
directly, outside any custodian. Before you can send funds to one, the end
customer must prove that they own it. Register the wallet with its ownership
details, then have the end customer complete one of two ownership checks:

* **Message signature**: The end customer signs a Circle-provided message with
  the wallet's private key. Available for EVM blockchains and Solana.
* **Test deposit**: The end customer sends a small deposit, called a satoshi
  test, from the wallet. It confirms they control the private key. It's the
  fallback for all other blockchains, and remains available on EVM blockchains
  and Solana.

### Register the wallet with its ownership

Call `POST /v1/addresses/recipient` with the wallet's `address` and `chain`, the
end customer's `riskSignals`, and an `ownership` object. For a self-hosted
wallet, set `type` to `first_party`, set `custody.type` to `self_hosted`, and
omit `vaspId`.

> [!NOTE]
>
> Self-hosted wallets support first-party ownership only. The wallet must belong
> to the end customer; you can't register a `third_party` self-hosted wallet.

The SCA headers (`X-Sca-Challenge-Id` and `X-Sca-Assertion`) are required only
for end customers in the EEA. Omit them otherwise.

```shell theme={null}
curl --request POST \
  --url https://api-sandbox.circle.com/v1/addresses/recipient \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'X-Sca-Challenge-Id: {scaChallengeId}' \
  --header 'X-Sca-Assertion: {scaAssertion}' \
  --header 'Content-Type: application/json' \
  --data '
{
  "idempotencyKey": "'"$(uuidgen)"'",
  "address": "0x8f3b2c1a9d4e4f6ab7c81e2d3f4a5b6c7d8e9f01",
  "chain": "ETH",
  "currency": "USD",
  "description": "End customer self-hosted wallet",
  "clientEntityId": "{clientEntityId}",
  "riskSignals": {
    "ipAddress": "{endUserIp}",
    "sessionId": "{sessionId}",
    "deviceId": "{deviceId}"
  },
  "ownership": {
    "type": "first_party",
    "custody": { "type": "self_hosted" }
  }
}
'
```

The address is created in `pending_verification` and the response includes a
`verificationChallenge`. On EVM-family blockchains and Solana, the challenge
carries both a `messageSignature` block and a `satoshiTest` block, so you can
complete either check. On all other blockchains, only `satoshiTest` is present.

```json theme={null}
{
  "data": {
    "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "address": "0x8f3b2c1a9d4e4f6ab7c81e2d3f4a5b6c7d8e9f01",
    "chain": "ETH",
    "currency": "USD",
    "description": "End customer self-hosted wallet",
    "status": "pending_verification",
    "verificationChallenge": {
      "messageSignature": {
        "message": "example.circle.com wants you to sign in with your Ethereum account:\n0x8f3b2c1a9d4e4f6ab7c81e2d3f4a5b6c7d8e9f01\n\nVerify ownership of this address.\n\nURI: https://example.circle.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2026-10-22T17:00:00Z",
        "status": "active",
        "expiresAt": "2026-10-23T17:00:00Z"
      },
      "satoshiTest": {
        "destinationAddress": "0x1234567890abcdef1234567890abcdef12345678",
        "paymentId": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
        "amount": { "amount": "0.42", "currency": "USD" },
        "status": "active"
      }
    }
  }
}
```

### Verify ownership of the wallet

Complete one of the two checks. On EVM-family blockchains and Solana, the
message-signature check and the satoshi test are available. On all other
blockchains, use the satoshi test.

#### Sign a message

Have the end customer sign the `verificationChallenge.messageSignature.message`
string exactly as returned, with the wallet's private key:

* **EVM-family blockchains**: Produce a `personal_sign` signature (EIP-191) and
  submit the `0x`-prefixed hex string.
* **Solana**: Produce an `ed25519` signature over the raw UTF-8 message bytes,
  with no hashing and no prefix, and submit the 64-byte signature as Base58 or
  Base64.

Then submit the signature with
`POST /v1/addresses/recipient/{recipientAddressId}/verification/signature`:

```shell theme={null}
curl --request POST \
  --url https://api-sandbox.circle.com/v1/addresses/recipient/{recipientAddressId}/verification/signature \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '
{
  "signature": "0x4c8a3b...a1b2c3"
}
'
```

A `200` response means the signature verified and the address moved out of
`pending_verification`. A `400` response means the signature was invalid or the
address was already verified.

If `messageSignature.status` becomes `expired` before you submit, request a
fresh challenge with
`POST /v1/addresses/recipient/{recipientAddressId}/verification/resend`, then
have the end customer sign the new `message`. A resend issues new challenges for
both methods.

#### Send a satoshi test

Have the end customer send exactly `satoshiTest.amount.amount` from their
self-hosted wallet to `satoshiTest.destinationAddress`, in USDC for a `USD`
address or in EURC for an `EUR` address. If the amount or token doesn't match,
the address stays `pending_verification`. If the chain requires a memo or tag,
include `satoshiTest.paymentId`.

If `satoshiTest.status` becomes `expired` before the deposit arrives, request a
fresh one with
`POST /v1/addresses/recipient/{recipientAddressId}/verification/resend`, then
repeat the deposit against the new `satoshiTest.destinationAddress` and
`satoshiTest.amount` values.

### Poll until verification completes

Poll the address until its `status` moves to `verification_succeeded`:

```shell theme={null}
curl --request GET \
  --url https://api-sandbox.circle.com/v1/addresses/recipient/{recipientAddressId} \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

When verification completes, the response `status` changes to
`verification_succeeded` and the `verificationChallenge` is omitted:

```json theme={null}
{
  "data": {
    "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "address": "0x8f3b2c1a9d4e4f6ab7c81e2d3f4a5b6c7d8e9f01",
    "chain": "ETH",
    "currency": "USD",
    "description": "End customer self-hosted wallet",
    "status": "verification_succeeded"
  }
}
```

Once the recipient address is `verification_succeeded`, it's eligible for
transfers. A verified self-hosted wallet stays in this status. It doesn't move
to `active`. See
[Send crypto transfers](https://developers.circle.com/digital-asset-accounts/howtos/crypto-transfers.md).

## List recipient addresses

Retrieve an end customer's verified recipient addresses, which are eligible for
transfers, with
[`GET /v1/addresses/recipient`](https://developers.circle.com/api-reference/digital-asset-accounts/all/list-recipient-addresses).
This endpoint supports pagination. To check an address that is still being
verified, get it by `id` with
[`GET /v1/addresses/recipient/{id}`](https://developers.circle.com/api-reference/digital-asset-accounts/all/get-recipient-address).

```shell theme={null}
curl --request GET \
  --url 'https://api-sandbox.circle.com/v1/addresses/recipient?clientEntityId={clientEntityId}&pageSize=25' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

**Response**

```json theme={null}
{
  "data": [
    {
      "id": "c7d8e9f0-a1b2-3456-7890-abcdef123456",
      "address": "0x1234567890abcdef1234567890abcdef12345678",
      "chain": "ETH",
      "currency": "USD",
      "description": "Treasury wallet",
      "status": "active"
    }
  ]
}
```

## Delete a recipient address

Remove a recipient address that is no longer needed, with
[`DELETE /v1/addresses/recipient/{id}`](https://developers.circle.com/api-reference/digital-asset-accounts/all/delete-recipient-address).
You can't delete an address whose `status` is `failed`, `disabled`, or
`verification_succeeded`. The API returns a `400` error. Deleted addresses
cannot be used for new transfers.

```shell theme={null}
curl --request DELETE \
  --url https://api-sandbox.circle.com/v1/addresses/recipient/{recipientAddressId} \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

If the deletion is successful, the API returns a `200` response.

For an end customer in the EEA, also send the `X-Sca-Challenge-Id` and
`X-Sca-Assertion` headers from an `ADDRESS_BOOK_DELETE` challenge. See
[Implement SCA](https://developers.circle.com/digital-asset-accounts/howtos/strong-customer-authentication.md).

> [!NOTE]
>
> Deleting a recipient address does not affect previously completed transfers to
> that address. The transaction history is preserved.

## See also

* [Send crypto transfers](https://developers.circle.com/digital-asset-accounts/howtos/crypto-transfers.md)
* [EEA API behavior](https://developers.circle.com/digital-asset-accounts/references/eea-api-behavior.md)
