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

# Quickstart: Send stablecoin payouts

> Send USDC payouts with Managed Payments using the address book and optional main-wallet funding

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](/cpn/managed-payments) and
  [Sub-wallet architecture](/cpn/managed-payments/concepts/sub-wallet-architecture).
* (Optional) A webhook endpoint for address book and payout events. See
  [Set up a webhook endpoint](/api-reference/webhook-endpoints#v1-notifications).

## Sequence diagram

```mermaid theme={null}
sequenceDiagram
    participant Server as Your server
    participant CAPI as Circle APIs
    Server->>CAPI: POST v1/addressBook/recipients
    CAPI-->>Server: Recipient pending or inactive
    CAPI-->>Server: Webhook or poll recipient active
    Server->>CAPI: POST v1/payouts
    CAPI-->>Server: Payout pending
    CAPI-->>Server: Webhook or poll payout complete
```

## 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](/api-reference/cpn/managed-payments/address-book/create-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).

<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.
</Note>

<CodeGroup>
  ```curl cURL theme={null}
  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"
    }
  }'
  ```

  ```typescript TypeScript theme={null}
  /**
   * See installation instructions at
   * https://developers.circle.com/circle-mint/circle-sdks
   */
  import {
    AddressBookRecipientRequest,
    Chain,
    Circle,
    CircleEnvironments,
  } from "@circle-fin/circle-sdk";
  import crypto from "crypto";

  const circle = new Circle("<your-api-key>", CircleEnvironments.sandbox);

  async function createAddressBookRecipient(): Promise<void> {
    const reqBody: AddressBookRecipientRequest = {
      idempotencyKey: crypto.randomUUID(),
      chain: Chain.Eth,
      address: "0x65BFCf1a6289a0b77b4D3F7d12005a05949FD8C3",
      metadata: {
        email: "satoshi@circle.com",
        bns: "testbns",
        nickname: "test nickname desc",
      },
    };
    const resp =
      await circle.cryptoAddressBook.createAddressBookRecipient(reqBody);
    console.log(resp.data);
  }

  void createAddressBookRecipient();
  ```
</CodeGroup>

```json Response theme={null}
{
  "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.

```json Address book recipient webhook notification theme={null}
{
  "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](/api-reference/cpn/managed-payments/address-book/get-address-book-recipient)
until `data.status` is `active`.

<CodeGroup>
  ```curl cURL theme={null}
  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}'
  ```

  ```typescript TypeScript theme={null}
  /**
   * See installation instructions at
   * https://developers.circle.com/circle-mint/circle-sdks
   */
  import { Circle, CircleEnvironments } from "@circle-fin/circle-sdk";

  const circle = new Circle("<your-api-key>", CircleEnvironments.sandbox);

  function delay(ms: number): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
  }

  async function pollRecipientActive(recipientId: string): Promise<void> {
    const pollIntervalMs = 500;
    while (true) {
      const resp =
        await circle.cryptoAddressBook.getAddressBookRecipient(recipientId);
      const status = resp.data?.data?.status;
      if (status === "active") {
        console.log(resp.data);
        return;
      }
      await delay(pollIntervalMs);
    }
  }

  void pollRecipientActive("dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe");
  ```
</CodeGroup>

```json Response theme={null}
{
  "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](/api-reference/cpn/managed-payments/payouts/create-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**.

<Warning>
  **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](/circle-mint/howtos/transfer-on-chain#travel-rule-compliance)
  compliance.
</Warning>

<Note>
  **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](/api-reference/cpn/managed-payments/wires/create-account-wire-account)
  and [Settlement flows](/cpn/managed-payments/concepts/settlement-flows).
</Note>

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.

<CodeGroup>
  ```curl cURL theme={null}
  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"
    }
  }'
  ```

  ```typescript TypeScript theme={null}
  /**
   * See installation instructions at
   * https://developers.circle.com/circle-mint/circle-sdks
   */
  import {
    Circle,
    CircleEnvironments,
    CryptoPayoutCreationRequest,
    CryptoPayoutDestinationType,
    IdentityTypeEnum,
    MoneyCurrencyEnum,
    ToAmountCurrencyEnum,
    TransferSourceWalletLocationTypeEnum,
  } from "@circle-fin/circle-sdk";
  import crypto from "crypto";

  /** Managed Payments: `useMainWalletFunding` on `source` (SDK types may lag the API). */
  type PayoutSourceWallet = NonNullable<CryptoPayoutCreationRequest["source"]> & {
    useMainWalletFunding?: boolean;
  };

  const circle = new Circle("<your-api-key>", CircleEnvironments.sandbox);

  async function createManagedPaymentsPayout(): Promise<void> {
    const sourceWallet: PayoutSourceWallet = {
      type: TransferSourceWalletLocationTypeEnum.Wallet,
      id: "12345",
      useMainWalletFunding: true,
      identities: [
        {
          type: IdentityTypeEnum.Individual,
          name: "Satoshi Nakamoto",
          addresses: [
            {
              line1: "100 Money Street",
              line2: "Suite 1",
              city: "Boston",
              district: "MA",
              postalCode: "01234",
              country: "US",
            },
          ],
        },
      ],
    };

    const reqBody: CryptoPayoutCreationRequest = {
      idempotencyKey: crypto.randomUUID(),
      source: sourceWallet,
      destination: {
        type: CryptoPayoutDestinationType.AddressBook,
        id: "dff5fcb3-2e52-5c13-8a66-0a5be9c7ecbe",
      },
      amount: {
        amount: "3000.14",
        currency: MoneyCurrencyEnum.Usd,
      },
      toAmount: {
        currency: ToAmountCurrencyEnum.Usd,
      },
    };

    const resp = await circle.payouts.createPayout(reqBody);
    console.log(resp.data);
  }

  void createManagedPaymentsPayout();
  ```
</CodeGroup>

```json Response theme={null}
{
  "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](/api-reference/cpn/managed-payments/payouts/get-payout) to
confirm the payout finished onchain.

### Option 1: Webhook notification

```json Payout webhook notification theme={null}
{
  "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

<CodeGroup>
  ```curl cURL theme={null}
  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}'
  ```

  ```typescript TypeScript theme={null}
  /**
   * See installation instructions at
   * https://developers.circle.com/circle-mint/circle-sdks
   */
  import { Circle, CircleEnvironments } from "@circle-fin/circle-sdk";

  const circle = new Circle("<your-api-key>", CircleEnvironments.sandbox);

  function delay(ms: number): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
  }

  async function pollPayoutComplete(payoutId: string): Promise<void> {
    const pollIntervalMs = 500;
    while (true) {
      const resp = await circle.payouts.getPayout(payoutId);
      const status = resp.data?.data?.status;
      if (status === "complete") {
        console.log(resp.data);
        return;
      }
      await delay(pollIntervalMs);
    }
  }

  void pollPayoutComplete("b8627ae8-732b-4d25-b947-1df8f4007a29");
  ```
</CodeGroup>

```json Response theme={null}
{
  "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"
  }
}
```
