> ## 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: Receive a stablecoin payin

> Accept an onchain USDC or EURC payin into your Circle Mint account by creating a payment intent, sharing the deposit address, and confirming the payment.

Use the Stablecoin Payins API to accept an onchain USDC or EURC payment into
your Circle Mint account. You create a payment intent, share the deposit address
that Circle assigns, and then confirm the payment after the customer transfers
funds. For background on how payment intents and payments relate to one another,
see
[Stablecoin payins and payouts](/cpn/stablecoin-payments/concepts/how-stablecoin-payments-work).

## Prerequisites

Before you begin, ensure that you've:

* Enabled Stablecoin Payins on your Circle Mint account. Activation is available
  for the Americas (Circle LLC) and EEA (Circle SAS, `CIRCLE_FR`). Contact your
  Circle representative or reach out through the
  [Circle Mint contact form](https://www.circle.com/mint-contact) to request
  access.
* Obtained a Circle Mint sandbox API key. See
  [Getting started](/circle-mint/quickstarts/getting-started) for setup.
* Selected a blockchain that the Stablecoin Payins API supports. The Payins API
  supports a narrower set of blockchains than the Payouts API. See
  [Supported chains and currencies](/cpn/stablecoin-payments/references/supported-blockchains)
  to confirm coverage.
* Obtained a `merchantWalletId` for the wallet that receives settled funds. You
  don't need a funded Mint account for this flow.

## Steps

<Steps>
  <Step title="Create a payment intent">
    Send a `POST` request to `/v1/paymentIntents` to create a payment intent. Use
    **continuous** mode, the default, unless you need a fixed-amount,
    single-checkout intent.

    The required fields differ by Circle entity. Select the tab for the Circle
    entity that books your payins.

    <Tabs>
      <Tab title="Circle LLC (US)">
        ```bash theme={null}
        curl -X POST https://api-sandbox.circle.com/v1/paymentIntents \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "idempotencyKey": "17607606-e383-4874-87c3-7e46a5dc03dd",
            "currency": "USD",
            "settlementCurrency": "USD",
            "merchantWalletId": "1000999922",
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ]
          }'
        ```

        The response includes the intent `id` and a `timeline` showing the `created`
        status. The deposit address is not yet populated.

        ```json theme={null}
        {
          "data": {
            "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "currency": "USD",
            "settlementCurrency": "USD",
            "amountPaid": { "amount": "0.00", "currency": "USD" },
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ],
            "timeline": [
              {
                "status": "created",
                "time": "2026-04-12T20:13:35.579331Z"
              }
            ],
            "type": "continuous",
            "createDate": "2026-04-12T20:13:35.578678Z",
            "updateDate": "2026-04-12T20:13:35.578678Z"
          }
        }
        ```

        <Accordion title="Transient variant">
          To create a fixed-amount, single-use intent, set `type` to `transient` and
          include an `amount`. The deposit address that Circle assigns is single-use and
          the intent expires after `expiresOn`. Send the same `POST /v1/paymentIntents`
          request with this body instead:

          ```json theme={null}
          {
            "idempotencyKey": "17607606-e383-4874-87c3-7e46a5dc03dd",
            "type": "transient",
            "amount": {
              "amount": "1.00",
              "currency": "USD"
            },
            "settlementCurrency": "USD",
            "merchantWalletId": "1000999922",
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ]
          }
          ```
        </Accordion>
      </Tab>

      <Tab title="Circle SAS (CIRCLE_FR)">
        `purposeOfTransfer` and `beneficiaryLei` are required. Use a
        [payment reason code](/cpn/references/errors/payment-reason-codes) other than
        `PMT006` for `purposeOfTransfer`. `beneficiaryLei` is a 20-character Legal
        Entity Identifier (ISO 17442) identifying the receiving entity. For the full EEA
        data model, see
        [Travel Rule compliance](/circle-mint/references/travel-rule-compliance).

        ```bash theme={null}
        curl -X POST https://api-sandbox.circle.com/v1/paymentIntents \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "idempotencyKey": "17607606-e383-4874-87c3-7e46a5dc03dd",
            "currency": "USD",
            "settlementCurrency": "USD",
            "purposeOfTransfer": "PMT001",
            "beneficiaryLei": "529900T8BM49AURSDO55",
            "merchantWalletId": "1000999922",
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ]
          }'
        ```

        The response includes the intent `id` and a `timeline` showing the `created`
        status. The deposit address is not yet populated.

        ```json theme={null}
        {
          "data": {
            "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "currency": "USD",
            "settlementCurrency": "USD",
            "amountPaid": { "amount": "0.00", "currency": "USD" },
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ],
            "timeline": [
              {
                "status": "created",
                "time": "2026-04-12T20:13:35.579331Z"
              }
            ],
            "type": "continuous",
            "purposeOfTransfer": "PMT001",
            "createDate": "2026-04-12T20:13:35.578678Z",
            "updateDate": "2026-04-12T20:13:35.578678Z"
          }
        }
        ```

        <Accordion title="Transient variant">
          To create a fixed-amount, single-use intent, set `type` to `transient` and
          include an `amount`. The deposit address that Circle assigns is single-use and
          the intent expires after `expiresOn`. Send the same
          [`POST /v1/paymentIntents`](/api-reference/circle-mint/payments/create-payment-intent)
          request with this body instead:

          ```json theme={null}
          {
            "idempotencyKey": "17607606-e383-4874-87c3-7e46a5dc03dd",
            "type": "transient",
            "amount": {
              "amount": "1.00",
              "currency": "USD"
            },
            "settlementCurrency": "USD",
            "purposeOfTransfer": "PMT001",
            "merchantWalletId": "1000999922",
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE"
              }
            ]
          }
          ```
        </Accordion>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Get the deposit address">
    Circle assigns the deposit address asynchronously after the intent is created.
    Use webhooks or polling to retrieve it.

    <Tabs>
      <Tab title="Webhook">
        Subscribe to `paymentIntents` notifications. When Circle assigns the address,
        you receive a payload whose `paymentMethods[0].address` is populated and whose
        `timeline` advances to `pending`.

        ```json theme={null}
        {
          "notificationType": "paymentIntents",
          "version": 1,
          "paymentIntent": {
            "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE",
                "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
              }
            ],
            "timeline": [
              { "status": "pending", "time": "2026-04-12T20:13:38.188286Z" },
              { "status": "created", "time": "2026-04-12T20:13:35.579331Z" }
            ]
          }
        }
        ```
      </Tab>

      <Tab title="Polling">
        Poll `GET /v1/paymentIntents/{id}` until `paymentMethods[0].address` appears in
        the response.

        ```bash theme={null}
        curl -X GET https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b \
          -H "Authorization: Bearer $API_KEY"
        ```

        ```json theme={null}
        {
          "data": {
            "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "currency": "USD",
            "settlementCurrency": "USD",
            "amountPaid": { "amount": "0.00", "currency": "USD" },
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE",
                "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
              }
            ],
            "timeline": [
              { "status": "pending", "time": "2026-04-12T20:13:38.188286Z" },
              { "status": "created", "time": "2026-04-12T20:13:35.579331Z" }
            ],
            "type": "continuous"
          }
        }
        ```
      </Tab>
    </Tabs>

    For memo-based blockchains such as Stellar (XLM) and Hedera (HBAR), the payment
    method also includes an `addressTag`. If your customer's wallet enforces memos,
    they must include this tag when sending funds.
  </Step>

  <Step title="Customer transfers funds">
    Display the deposit address (and `addressTag` if applicable) to your customer.
    The customer sends USDC or EURC to that address from their wallet. This step
    takes place outside the API.

    <Warning>
      The deposit address is valid only on the blockchain assigned to the payment
      intent. Funds sent on any other blockchain are not credited and are
      permanently lost.
    </Warning>
  </Step>

  <Step title="Confirm the payment">
    After the onchain transfer settles, Circle creates a `payment` object linked to
    the intent. The intent's own state machine differs by mode:

    * **Continuous intents** stay at `active`. The intent timeline does not advance
      to `complete`, and the intent's `paymentIds` array is not populated. Reconcile
      each settled transfer through the `payments` webhook or by listing payments
      with `GET /v1/payments?paymentIntentId={id}`.
    * **Transient intents** transition to `complete` after the single expected
      transfer settles. The latest timeline entry carries a context of `paid`,
      `underpaid`, or `overpaid`, and the intent's `paymentIds` array lists the
      settled payments.

    <Tabs>
      <Tab title="Webhook (any intent)">
        A `payments` notification fires whenever an inbound transfer settles. The
        payload includes the payment `status`, the originating `fromAddresses`, the
        onchain `transactionHash`, and the `paymentIntentId` that links the payment back
        to its intent. This works the same for continuous and transient intents.

        ```json theme={null}
        {
          "notificationType": "payments",
          "version": 1,
          "payment": {
            "id": "66c56b6a-fc79-338b-8b94-aacc4f0f18de",
            "type": "payment",
            "status": "paid",
            "amount": { "amount": "1.00", "currency": "USD" },
            "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "fromAddresses": {
              "chain": "BASE",
              "addresses": ["0x0d4344cff68f72a5b9abded37ca5862941a62050"]
            },
            "depositAddress": {
              "chain": "BASE",
              "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
            },
            "transactionHash": "0x7351585460bd657f320b9afa02a52c26d89272d0d10cc29913eb8b28e64fd906"
          }
        }
        ```

        For transient intents only, a `paymentIntents` notification also fires when the
        intent reaches `complete`, with a new `timeline` entry whose `status` is
        `complete` and whose `context` describes the outcome (`paid`, `underpaid`, or
        `overpaid`). Continuous intents do not produce this notification.
      </Tab>

      <Tab title="Polling (continuous)">
        For continuous intents, list the payments scoped to the intent. Each settled
        transfer appears as its own payment.

        ```bash theme={null}
        curl -X GET "https://api-sandbox.circle.com/v1/payments?paymentIntentId=e2e90ba3-9d1f-490d-9460-24ac6eb55a1b" \
          -H "Authorization: Bearer $API_KEY"
        ```

        ```json theme={null}
        {
          "data": [
            {
              "id": "66c56b6a-fc79-338b-8b94-aacc4f0f18de",
              "type": "payment",
              "status": "paid",
              "amount": { "amount": "1.00", "currency": "USD" },
              "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
              "fromAddresses": {
                "chain": "BASE",
                "addresses": ["0x0d4344cff68f72a5b9abded37ca5862941a62050"]
              },
              "depositAddress": {
                "chain": "BASE",
                "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
              },
              "transactionHash": "0x7351585460bd657f320b9afa02a52c26d89272d0d10cc29913eb8b28e64fd906"
            }
          ]
        }
        ```

        Polling `GET /v1/paymentIntents/{id}` directly does not work for continuous
        intents because the intent's timeline does not advance to `complete` and
        `paymentIds` is not populated.
      </Tab>

      <Tab title="Polling (transient)">
        For transient intents, poll `GET /v1/paymentIntents/{id}` until the newest
        `timeline` entry has `status` `complete`. The `context` field on that entry
        tells you whether the intent was paid, underpaid, or overpaid. Use the IDs in
        `paymentIds` to fetch the underlying payment records.

        ```bash theme={null}
        curl -X GET https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b \
          -H "Authorization: Bearer $API_KEY"
        ```

        ```json theme={null}
        {
          "data": {
            "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "currency": "USD",
            "settlementCurrency": "USD",
            "amount": { "amount": "1.00", "currency": "USD" },
            "amountPaid": { "amount": "1.00", "currency": "USD" },
            "amountRefunded": { "amount": "0.00", "currency": "USD" },
            "paymentMethods": [
              {
                "type": "blockchain",
                "chain": "BASE",
                "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
              }
            ],
            "paymentIds": ["66c56b6a-fc79-338b-8b94-aacc4f0f18de"],
            "refundIds": [],
            "timeline": [
              {
                "status": "complete",
                "context": "paid",
                "time": "2026-04-12T20:19:24.861094Z"
              },
              { "status": "pending", "time": "2026-04-12T20:13:38.188286Z" },
              { "status": "created", "time": "2026-04-12T20:13:35.579331Z" }
            ],
            "type": "transient"
          }
        }
        ```

        Fetch the payment detail with `GET /v1/payments/{id}` to read the onchain
        `transactionHash` and the sender's `fromAddresses`.

        ```bash theme={null}
        curl -X GET https://api-sandbox.circle.com/v1/payments/66c56b6a-fc79-338b-8b94-aacc4f0f18de \
          -H "Authorization: Bearer $API_KEY"
        ```

        ```json theme={null}
        {
          "data": {
            "id": "66c56b6a-fc79-338b-8b94-aacc4f0f18de",
            "type": "payment",
            "status": "paid",
            "amount": { "amount": "1.00", "currency": "USD" },
            "fees": { "amount": "0.01", "currency": "USD" },
            "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "settlementAmount": { "amount": "1.00", "currency": "USD" },
            "fromAddresses": {
              "chain": "BASE",
              "addresses": ["0x0d4344cff68f72a5b9abded37ca5862941a62050"]
            },
            "depositAddress": {
              "chain": "BASE",
              "address": "0x97de855690955e0da79ce5c1b6804847e7070c7f"
            },
            "transactionHash": "0x7351585460bd657f320b9afa02a52c26d89272d0d10cc29913eb8b28e64fd906"
          }
        }
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="(Optional) Expire a transient intent">
    If a customer abandons checkout, you can expire a transient payment intent
    manually by calling `POST /v1/paymentIntents/{id}/expire`. This action applies
    only to transient intents.

    ```bash theme={null}
    curl -X POST https://api-sandbox.circle.com/v1/paymentIntents/6e4d4047-db14-4c09-b238-1215aee50d03/expire \
      -H "Authorization: Bearer $API_KEY"
    ```

    <Warning>
      Funds sent to the deposit address after the intent expires are still credited
      to your Circle Mint account, but they do not match the original intent. You
      may need to contact Circle Support to reconcile these transfers.
    </Warning>
  </Step>
</Steps>

## Handle completion contexts (transient intents)

Completion contexts apply only to transient intents, because they have a target
amount to compare against. Continuous intents stay at `active` and never reach
`complete`, so no context is produced.

When a transient intent reaches `complete`, the `context` on the latest timeline
entry tells you how the amount paid compares to what was expected. Each context
calls for a different downstream action.

| Context | Meaning | Typical handling |
| - | - | - |
| `paid` | The customer paid the expected amount. For transient intents, `amountPaid` equals `amount`. | Fulfill the order. |
| `underpaid` | The customer paid less than the expected `amount` on a transient intent. | Refund the partial payment or contact the customer to top up. See [Refund a stablecoin payin](/cpn/stablecoin-payments/howtos/refund-stablecoin-payin). |
| `overpaid` | The customer paid more than expected. | Refund the excess to the sender, or refund the full amount and ask the customer to retry. |

## See also

* [Stablecoin payins and payouts](/cpn/stablecoin-payments/concepts/how-stablecoin-payments-work):
  Concepts for payment intents, payments, and refunds.
* [Refund a stablecoin payin](/cpn/stablecoin-payments/howtos/refund-stablecoin-payin):
  How to issue a full or partial refund.
* [Supported chains and currencies](/cpn/stablecoin-payments/references/supported-blockchains):
  Confirm which blockchains the Payins API supports.
* [Set up a webhook endpoint](/api-reference/webhook-endpoints#v1-notifications):
  Subscribe to `paymentIntents` and `payments` webhooks.
