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

> Issue a full or partial refund against a settled Stablecoin Payins payment intent using the Circle Mint refund APIs.

Use the Stablecoin Payins API to return funds against a payment intent that has
already received at least one settled payment. You can issue a single full
refund, a single partial refund, or multiple partial refunds up to the settled
total. For the mental model that underpins this procedure, see
[Stablecoin payins and payouts](/cpn/stablecoin-payments/concepts/how-stablecoin-payments-work).

<Warning>
  Refunds are subject to four hard constraints:

  * You have 30 days from the payment intent's creation date to start a refund.
  * At least one payment on the intent must be settled. You cannot start a refund
    while a payment is still pending.
  * Once a refund starts, the payment intent transitions to `refunded` and stops
    accepting new payins. Treat the intent as terminal and create a new intent for
    any future checkout.
  * Refunds initiated from the Circle Mint console cannot be canceled after
    submission.
</Warning>

Regional availability also affects which payment intents are refundable today.

<Note>
  Refunds for Stablecoin Payins booked through Circle Singapore (`CIRCLE_SG`)
  are not supported. Refunds for Circle SAS (`CIRCLE_FR`) accounts are supported
  with restrictions. See Step 2 for details.
</Note>

## Prerequisites

Before you begin, ensure that you've:

* Confirmed that a settled payment exists on the payment intent you want to
  refund. If you haven't accepted a payin yet, see
  [Receive a stablecoin payin](/cpn/stablecoin-payments/howtos/receive-stablecoin-payin).
* Identified a refund destination address you control on the same blockchain as
  the original payment. The destination is merchant-supplied; Circle doesn't
  automatically return funds to the source wallet. For `CIRCLE_FR` accounts, see
  Step 2 for destination requirements.
* Obtained a Circle Mint sandbox API key. See
  [Getting started](/circle-mint/quickstarts/getting-started) for setup.

## Steps

<Steps>
  <Step title="Inspect the intent to confirm a settled payment">
    Before you initiate a refund, retrieve the payment intent and confirm that at
    least one payment in `paymentIds` is settled. Send a `GET` request to
    `/v1/paymentIntents/{id}`.

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

    A refund-eligible response has a populated `paymentIds` array and a non-zero
    `amountPaid`. The latest `timeline` entry shows `status` `complete` with
    `context` `paid`.

    ```json theme={null}
    {
      "data": {
        "id": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
        "currency": "USD",
        "settlementCurrency": "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": "continuous",
        "createDate": "2026-04-12T20:13:35.578678Z",
        "updateDate": "2026-04-12T20:19:24.861094Z"
      }
    }
    ```
  </Step>

  <Step title="Issue the refund">
    Send a `POST` request to `/v1/paymentIntents/{id}/refund`. The body specifies
    the destination you control, the refund amount, and an idempotency key. The
    request uses two amount fields:

    * `amount` carries the source-currency basis; only `currency` is required, and
      the value is derived from `toAmount`.
    * `toAmount` carries the actual refund amount that reaches the destination and
      must include both `amount` and `currency`.

    For memo-based blockchains, add an `addressTag` to the `destination` object.

    For `CIRCLE_FR` accounts, three requirements differ from Circle LLC behavior:

    * `paymentId` (required): include the ID of the settled payment from
      `paymentIds[]` on the intent. Circle uses this to associate the refund with
      the original payin and inherit its Travel Rule data.
    * Destination must be the original sender: set `destination` to
      `{ "useOriginalSender": true }`. Raw address destinations are rejected with
      error `1166` (`ADDRESS_BOOK_DESTINATION_REQUIRED`).
    * Cross-VASP refunds: refunds to a wallet address at a different VASP than the
      original payin are not supported.

    Add `paymentId` and the `useOriginalSender` destination to the request body:

    ```json theme={null}
    {
      "idempotencyKey": "9aed1aab-292a-427f-aae1-e0e358fef1c9",
      "paymentId": "66c56b6a-fc79-338b-8b94-aacc4f0f18de",
      "destination": {
        "useOriginalSender": true
      },
      "amount": {
        "currency": "USD"
      },
      "toAmount": {
        "amount": "0.50",
        "currency": "USD"
      }
    }
    ```

    For the full EEA data model, see
    [Travel Rule compliance](/circle-mint/references/travel-rule-compliance).

    <Tabs>
      <Tab title="Partial refund">
        Specify an amount lower than the settled total to return a portion of the
        payment.

        ```bash theme={null}
        curl -X POST https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b/refund \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d @body.json
        ```

        ```json theme={null}
        {
          "idempotencyKey": "9aed1aab-292a-427f-aae1-e0e358fef1c9",
          "destination": {
            "chain": "BASE",
            "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
          },
          "amount": {
            "currency": "USD"
          },
          "toAmount": {
            "amount": "0.50",
            "currency": "USD"
          }
        }
        ```
      </Tab>

      <Tab title="Full refund">
        Specify the full settled amount to return the entire payment.

        ```bash theme={null}
        curl -X POST https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b/refund \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d @body.json
        ```

        ```json theme={null}
        {
          "idempotencyKey": "9aed1aab-292a-427f-aae1-e0e358fef1c9",
          "destination": {
            "chain": "BASE",
            "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
          },
          "amount": {
            "currency": "USD"
          },
          "toAmount": {
            "amount": "1.00",
            "currency": "USD"
          }
        }
        ```
      </Tab>
    </Tabs>

    The response is a `payment` object with `type` `refund` and `status` `pending`.
    The `depositAddress` field shows where Circle sends the onchain transfer.

    ```json theme={null}
    {
      "data": {
        "id": "3389f4ba-aafd-4eef-aaa2-3292df8f62e6",
        "type": "refund",
        "status": "pending",
        "amount": { "currency": "USD" },
        "createDate": "2026-04-13T15:29:58.000000Z",
        "updateDate": "2026-04-13T15:29:58.000000Z",
        "merchantId": "f1397191-56e6-42fd-be86-0a7b9bd91522",
        "merchantWalletId": "1000999922",
        "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
        "settlementAmount": { "amount": "0.50", "currency": "USD" },
        "depositAddress": {
          "chain": "BASE",
          "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
        }
      }
    }
    ```

    <Warning>
      The payment intent's `timeline` immediately gains a `refunded` entry and the
      intent becomes terminal. New payins sent to the original deposit address after
      this point may not match the intent. Circle Support may need to reconcile
      them.
    </Warning>
  </Step>

  <Step title="Track refund completion">
    Refunds settle asynchronously after the onchain transfer confirms. Use webhooks
    or polling to detect settlement.

    <Tabs>
      <Tab title="Webhook">
        Subscribe to `payments` notifications. The same channel delivers both inbound
        payment events and refund events. Use the `type` field to tell them apart.

        ```json theme={null}
        {
          "notificationType": "payments",
          "version": 1,
          "payment": {
            "id": "3389f4ba-aafd-4eef-aaa2-3292df8f62e6",
            "type": "refund",
            "status": "paid",
            "amount": { "currency": "USD" },
            "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "settlementAmount": { "amount": "0.50", "currency": "USD" },
            "depositAddress": {
              "chain": "BASE",
              "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
            },
            "transactionHash": "0xa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
          }
        }
        ```
      </Tab>

      <Tab title="Polling">
        Poll `GET /v1/payments/{id}` with the refund `id` from the previous step.

        ```bash theme={null}
        curl -X GET https://api-sandbox.circle.com/v1/payments/3389f4ba-aafd-4eef-aaa2-3292df8f62e6 \
          -H "Authorization: Bearer $API_KEY"
        ```

        The refund is settled when `status` is `paid`. The response also includes the
        onchain `transactionHash`.

        ```json theme={null}
        {
          "data": {
            "id": "3389f4ba-aafd-4eef-aaa2-3292df8f62e6",
            "type": "refund",
            "status": "paid",
            "amount": { "currency": "USD" },
            "fees": { "amount": "0.01", "currency": "USD" },
            "createDate": "2026-04-13T15:29:58.000000Z",
            "updateDate": "2026-04-13T15:35:12.000000Z",
            "merchantId": "f1397191-56e6-42fd-be86-0a7b9bd91522",
            "merchantWalletId": "1000999922",
            "paymentIntentId": "e2e90ba3-9d1f-490d-9460-24ac6eb55a1b",
            "settlementAmount": { "amount": "0.50", "currency": "USD" },
            "depositAddress": {
              "chain": "BASE",
              "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
            },
            "transactionHash": "0xa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
          }
        }
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="(Optional) Issue an additional partial refund">
    If the original refund was partial and the intent still has refundable balance,
    you can issue further partial refunds up to the total settled amount. Each
    additional refund still requires the intent to be within 30 days of its creation
    date, and each call must use a unique `idempotencyKey`.

    ```bash theme={null}
    curl -X POST https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b/refund \
      -H "Authorization: Bearer $API_KEY" \
      -H "Content-Type: application/json" \
      -d @body.json
    ```

    ```json theme={null}
    {
      "idempotencyKey": "b1d4ef72-3c8f-4cb2-9f0d-3c6f9e2a1d77",
      "destination": {
        "chain": "BASE",
        "address": "0xcd7475EAEd9EE9678Cf219CeC748e25aBA068A69"
      },
      "amount": {
        "currency": "USD"
      },
      "toAmount": {
        "amount": "0.25",
        "currency": "USD"
      }
    }
    ```
  </Step>
</Steps>
