Skip to main content

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.

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.
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.
Regional availability also affects which payment intents are refundable today.
Refunds for Stablecoin Payins booked through Circle Singapore (CIRCLE_SG) are not supported. See Travel Rule compliance for the data model that applies to Singapore stablecoin transactions.

Prerequisites

Before you begin:
  • A settled payment exists on the payment intent you want to refund. If you have not accepted a payin yet, see Receive a stablecoin payin.
  • You have a refund destination address that you control on the same blockchain as the original payment. The destination is merchant-supplied; Circle does not automatically return funds to the source wallet.
  • You have a Circle Mint sandbox API key. See Getting started for setup.

Steps

1

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}.
curl -X GET https://api-sandbox.circle.com/v1/paymentIntents/e2e90ba3-9d1f-490d-9460-24ac6eb55a1b \
  -H "Authorization: Bearer $API_KEY"
A refund-eligible response includes a populated paymentIds array, a non-zero amountPaid, and a timeline whose latest entry has status complete with context paid.
{
  "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"
  }
}
2

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.
Specify an amount lower than the settled total to return a portion of the payment.
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
{
  "idempotencyKey": "9aed1aab-292a-427f-aae1-e0e358fef1c9",
  "destination": {
    "chain": "BASE",
    "address": "0xcd7475eaed9ee9678cf219cec748e25aba068a69"
  },
  "amount": {
    "currency": "USD"
  },
  "toAmount": {
    "amount": "0.50",
    "currency": "USD"
  }
}
The response returns a payment object with type refund, status pending, and a depositAddress that holds the refund destination Circle will use for the onchain transfer.
{
  "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"
    }
  }
}
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 and can require Circle Support to reconcile.
3

Track refund completion

Refunds settle asynchronously after the onchain transfer confirms. Use webhooks or polling to detect settlement.
Subscribe to payments notifications. The same channel that delivers inbound payment events also delivers refund events, discriminated by the type field.
{
  "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"
  }
}
4

(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.
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
{
  "idempotencyKey": "b1d4ef72-3c8f-4cb2-9f0d-3c6f9e2a1d77",
  "destination": {
    "chain": "BASE",
    "address": "0xcd7475eaed9ee9678cf219cec748e25aba068a69"
  },
  "amount": {
    "currency": "USD"
  },
  "toAmount": {
    "amount": "0.25",
    "currency": "USD"
  }
}

See also