Skip to main content
This guide shows how to send a stablecoin payout with Managed Payments. You add a blockchain recipient to your address book. You wait until the recipient is active. You create a payout from a sub-wallet or the main wallet. You confirm that the payout completes.

Prerequisites

Before you start, ensure you have:
  • Managed Payments onboarding done and API keys for the environment you use. This guide uses the sandbox host api-sandbox.circle.com.
  • Roles for address book and stablecoin payouts for your setup. Circle sets roles when you onboard.
  • A sub-wallet wallet ID for source when you don’t pay from the main wallet alone. Read Managed Payments and Sub-wallet architecture.
  • (Optional) A webhook endpoint for address book and payout events. Use the notifications quickstart.

Sequence diagram

Steps

  1. Create an address book recipient
  2. Wait until the recipient is active
  3. Create a payout
  4. Confirm the payout completed

1. Create an address book recipient

Outbound sends use destinations in your address book. Create a recipient with create an address book recipient. Don’t create a payout until the recipient is active. The status you get when you create a recipient depends on your setup. For example, delayed withdrawals can keep a new entry inactive until it activates (see the following note).
Delayed withdrawals are off by default for Managed Payments. You can’t turn them on or off yourself; contact Circle customer support if your setup needs a different configuration. When delayed withdrawals are on, new address book entries start inactive. Wait until they’re active before you pay.
curl --location --request POST 'https://api-sandbox.circle.com/v1/addressBook/recipients' \
--header 'Accept: application/json' \
--header 'X-Request-Id: fb7980ad-fd01-468b-98ff-2d9ecff67f86' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data-raw '{
  "idempotencyKey": "9352ec9e-5ee6-441f-ab42-186bc71fbdde",
  "chain": "ETH",
  "address": "0x65BFCf1a6289a0b77b4D3F7d12005a05949FD8C3",
  "metadata": {
    "email": "satoshi@circle.com",
    "bns": "testbns",
    "nickname": "test nickname desc"
  }
}'
Response
{
  "data": {
    "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe",
    "chain": "ETH",
    "address": "0x65bfcf1a6289a0b77b4d3f7d12005a05949fd8c3",
    "metadata": {
      "nickname": "test nickname desc",
      "email": "satoshi@circle.com",
      "bns": "testbns"
    },
    "status": "pending",
    "updateDate": "2022-09-22T14:16:34.985353Z",
    "createDate": "2022-09-22T14:16:34.985353Z"
  }
}

2. Wait until the recipient is active

You cannot pay an inactive recipient. Use webhooks or polling until status is active.

Option 1: Webhook notification

After you subscribe, you get updates when address book recipients change. This sample shows an active recipient.
Address book recipient webhook notification
{
  "clientId": "a03a47ff-b0eb-4070-b3df-dc66752cc802",
  "notificationType": "addressBookRecipients",
  "version": 1,
  "customAttributes": {
    "clientId": "a03a47ff-b0eb-4070-b3df-dc66752cc802"
  },
  "addressBookRecipient": {
    "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe",
    "chain": "ETH",
    "address": "0x65bfcf1a6289a0b77b4d3f7d12005a05949fd8c3",
    "metadata": {
      "nickname": "test nickname desc",
      "email": "satoshi@circle.com",
      "bns": "testbns"
    },
    "status": "active",
    "updateDate": "2022-09-22T14:16:34.985353Z",
    "createDate": "2022-09-22T14:16:34.985353Z"
  }
}

Option 2: Poll the recipient

Call get an address book recipient until data.status is active.
curl --location --request GET 'https://api-sandbox.circle.com/v1/addressBook/recipients/dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe' \
--header 'Accept: application/json' \
--header 'X-Request-Id: 55990729-c59f-4cda-9edd-838cefaa1e42' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
  "data": {
    "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe",
    "chain": "ETH",
    "address": "0x65bfcf1a6289a0b77b4d3f7d12005a05949fd8c3",
    "metadata": {
      "nickname": "test nickname desc",
      "email": "satoshi@circle.com",
      "bns": "testbns"
    },
    "status": "active",
    "updateDate": "2022-09-22T14:16:34.985353Z",
    "createDate": "2022-09-22T14:16:34.985353Z"
  }
}

3. Create a payout

Create the payout with create a payout. Set destination to the address book entry. Set amount and optional toAmount. Set source when you use a sub-wallet. If you omit source, the API withdraws from the main wallet.
Receiving currency and Travel Rule: If you omit toAmount.currency, the API uses amount.currency as the receiving currency.The source.identities object is required for payouts greater than or equal to $3,000. The object describes the originator (your organization), not the recipient, for Financial Crimes Enforcement Network (FinCEN) Travel Rule compliance.
Main wallet funding: For Managed Payments, source.useMainWalletFunding defaults to true. When it is true, the API tops up from the main wallet if the source wallet lacks funds. Set source.useMainWalletFunding to false to require the full amount on the source wallet only.Insufficient funds: If a payout fails because funds are not ready yet, confirm your wire completed and USDC is available for the source wallet. See the Wires API and Settlement flows.
The example uses a sub-wallet as source, sets useMainWalletFunding on source, and uses an amount that needs identities for Travel Rule coverage. Replace placeholder IDs with your own values.
curl --location --request POST 'https://api-sandbox.circle.com/v1/payouts' \
--header 'Accept: application/json' \
--header 'X-Request-Id: ff422eab-52fa-4a6e-bf07-b6b522786468' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data-raw '{
  "idempotencyKey": "ba943ff1-ca16-49b2-ba55-1057e70ca5c7",
  "source": {
    "type": "wallet",
    "id": "12345",
    "useMainWalletFunding": true,
    "identities": [
      {
        "type": "individual",
        "name": "Satoshi Nakamoto",
        "addresses": [
          {
            "line1": "100 Money Street",
            "line2": "Suite 1",
            "city": "Boston",
            "district": "MA",
            "postalCode": "01234",
            "country": "US"
          }
        ]
      }
    ]
  },
  "destination": {
    "type": "address_book",
    "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe"
  },
  "amount": {
    "amount": "3000.14",
    "currency": "USD"
  },
  "toAmount": {
    "currency": "USD"
  }
}'
Response
{
  "data": {
    "id": "b8627ae8-732b-4d25-b947-1df8f4007a29",
    "sourceWalletId": "12345",
    "destination": {
      "type": "address_book",
      "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe"
    },
    "amount": {
      "amount": "3000.14",
      "currency": "USD"
    },
    "toAmount": {
      "currency": "USD"
    },
    "status": "pending",
    "updateDate": "2020-04-10T02:13:30.000Z",
    "createDate": "2020-04-10T02:13:30.000Z"
  }
}

4. Confirm the payout completed

Use webhooks or polling with get a payout to confirm the payout finished onchain.

Option 1: Webhook notification

Payout webhook notification
{
  "clientId": "a03a47ff-b0eb-4070-b3df-dc66752cc802",
  "notificationType": "payout",
  "version": 1,
  "customAttributes": {
    "clientId": "a03a47ff-b0eb-4070-b3df-dc66752cc802"
  },
  "payout": {
    "id": "b8627ae8-732b-4d25-b947-1df8f4007a29",
    "sourceWalletId": "12345",
    "destination": {
      "type": "address_book",
      "id": "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe"
    },
    "amount": {
      "amount": "3000.14",
      "currency": "USD"
    },
    "toAmount": {
      "amount": "3000.14",
      "currency": "USD"
    },
    "fees": {
      "amount": "0.00",
      "currency": "USD"
    },
    "networkFees": {
      "amount": "0.30",
      "currency": "USD"
    },
    "status": "complete",
    "createDate": "2020-04-10T02:13:30.000Z",
    "updateDate": "2020-04-10T02:13:30.000Z"
  }
}

Option 2: Poll the payout

curl --location --request GET 'https://api-sandbox.circle.com/v1/payouts/b8627ae8-732b-4d25-b947-1df8f4007a29' \
--header 'Accept: application/json' \
--header 'X-Request-Id: d36f3c00-9c98-4610-bfea-83995379995e' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
  "data": {
    "id": "2f3bca9a-2d0e-4aef-a511-026eefd3cc6f",
    "destination": {
      "type": "address_book",
      "id": "eaca84eb-69fb-53d3-9dac-f69cb5f1541a"
    },
    "amount": {
      "amount": "500.00",
      "currency": "USD"
    },
    "toAmount": {
      "amount": "500.00",
      "currency": "USD"
    },
    "externalRef": "0x41f8f2cd555e247716f3e9bc97366ac04a848fd2fde76732c894c6843fc6f8db",
    "createDate": "2023-08-14T20:30:19.145913Z",
    "updateDate": "2023-08-14T22:05:55.406292Z",
    "sourceWalletId": "1000594146",
    "fees": {
      "amount": "1.00",
      "currency": "USD"
    },
    "networkFees": {
      "amount": "0.01",
      "currency": "USD"
    },
    "status": "complete"
  }
}