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

# 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](/circle-mint/concepts/stablecoin-payins-and-payouts).

## Prerequisites

Before you begin:

* Stablecoin Payins is enabled on your Circle Mint account. Activation is scoped
  to the Americas (Circle LLC)—contact your Circle representative or reach out
  through the [Circle Mint contact form](https://www.circle.com/mint-contact) to
  request access.
* You have a Circle Mint sandbox API key. See
  [Getting started](/circle-mint/quickstarts/getting-started) for setup.
* You have 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](/circle-mint/references/supported-chains-and-currencies)
  to confirm coverage.
* You have a `merchantWalletId` for the wallet that receives settled funds. You
  do not need a funded Mint account—this is a receive 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.

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