> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Making a Payout

> Execute compliant payouts and handle compliance errors

The [Transfer Token API](/api-reference/wallets/transfer-token) enables you to trigger payouts from your treasury wallet to user wallets. The API automatically performs compliance checks and will return specific errors if the recipient has not provided the appropriate information or did not pass the necessary compliance checks.

<Warning>
  You must include `"transactionType": "regulated-transfer"` in your transfer request to enable compliance checks. Without it, the transfer defaults to `direct` mode, which skips all compliance screening. See [Supported Transfer Types](#supported-transfer-types) for all options.
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
      --url 'https://staging.crossmint.com/api/2025-06-09/wallets/<your-treasury-wallet-locator>/tokens/base-sepolia:usdc/transfers' \
      --header 'X-API-KEY: <your-server-api-key>' \
      --header 'Content-Type: application/json' \
      --data '{
          "recipient": "<recipient-wallet-address>",
          "amount": "100",
          "transactionType": "regulated-transfer"
      }'
  ```

  ```typescript Node.js theme={null}
  import { CrossmintWallets, createCrossmint } from "@crossmint/wallets-sdk";

  const crossmint = createCrossmint({
      apiKey: "<your-server-api-key>",
  });

  const crossmintWallets = CrossmintWallets.from(crossmint);

  const treasuryLocator = "<your-treasury-wallet-locator>";

  const treasuryWallet = await crossmintWallets.getWallet(treasuryLocator, {
      chain: "base-sepolia",
  });

  await treasuryWallet.useSigner({ type: "server", secret: process.env.WALLET_SIGNER_SECRET! });

  try {
      const { hash, explorerLink } = await treasuryWallet.send(
          "<recipient-wallet-address>",
          "usdc",
          "100",
          { transactionType: "regulated-transfer" }
      );

      console.log(`Transfer successful: ${hash}`);
      console.log(`Explorer: ${explorerLink}`);
  } catch (error) {
      console.error("Transfer failed:", error.message);
      // Handle compliance errors - see error codes below
  }
  ```

  ```python Python theme={null}
  import requests

  treasury_locator = "<your-treasury-wallet-locator>"
  token_locator = "base-sepolia:usdc"

  url = f"https://staging.crossmint.com/api/2025-06-09/wallets/{treasury_locator}/tokens/{token_locator}/transfers"

  payload = {
      "recipient": "<recipient-wallet-address>",
      "amount": "100",
      "transactionType": "regulated-transfer"
  }
  headers = {
      "X-API-KEY": "<your-server-api-key>",
      "Content-Type": "application/json"
  }

  response = requests.post(url, json=payload, headers=headers)

  print(response.json())
  ```
</CodeGroup>

<Note>
  **Signing transactions (REST API):** The cURL and Python examples above use the simplified [Transfer Token](/api-reference/wallets/transfer-token) endpoint. If your integration requires custom transaction signing, see the full create → sign → approve flow in [Send a Transaction (EVM) — REST tab](/wallets/guides/send-transaction-evm#rest).
</Note>

Wallet locator options for a treasury wallet:

* `<walletAddress>` (e.g., `0x1234...5678`)
* `chainType[:<walletType>]:alias:<alias>` (e.g., `evm:smart:alias:treasury`)

<Warning>
  Transfers are irreversible once executed. Clients must ensure that all transaction details are correct before submitting a transfer request.
</Warning>

## Compliance Errors

If a payout fails compliance checks, the API will return an error with a specific reason code. Here are the potential compliance-related errors:

<AccordionGroup>
  <Accordion title="recipient_personal_data_missing" icon="user-xmark">
    **Error Message:** "Required personal data is missing to complete regulated transfer"

    **Description:** The recipient wallet has not provided all personal data required for compliance screening. The recipient must complete their profile with first name, last name, date of birth, and country of residence.

    **Resolution:** Ensure the recipient has attached their personal data using the [user onboarding API](/identity/register-user-data#step-2-register-user-data).

    <Expandable title="How to test">
      In staging, send a transfer to any user wallet that has not had [personal data registered](/identity/register-user-data#step-2-register-user-data).
    </Expandable>

    ```jsonc theme={null}
    // Example error response
    {
        "error": {
            "reason": "recipient_personal_data_missing",
            "message": "Required personal data is missing to complete regulated transfer"
        }
    }
    ```
  </Accordion>

  <Accordion title="recipient_wallet_sanctioned" icon="shield-xmark">
    **Error Message:** "Recipient wallet address is sanctioned and can't receive assets"

    **Description:** The recipient's wallet address failed Elliptic sanctions screening. This indicates the address has been flagged in sanctions lists or is associated with prohibited activities.

    **Resolution:** The payout cannot proceed. The recipient's wallet address has been identified as high-risk and is blocked from receiving payouts.

    <Expandable title="How to test">
      In staging, you can trigger a sanctions error by using the following special addresses across different chains as the recipient:

      * EVM: `0xc9b888e8f2c9c60f72f84e97e4416c3aca3e1f7c`
      * Solana: `7H7Dph61J9W9WzvRUQbM1tsPJpEQhaQ3GVxPSj1EnSuw`
      * Stellar: `G5CLUPQPRWQXKHASBYSYMIPHE2ZMIFLMEH3ROBTOQMR24YWQIFQW2RY5`
    </Expandable>

    ```jsonc theme={null}
    // Example error response
    {
        "error": {
            "reason": "recipient_wallet_sanctioned",
            "message": "Recipient wallet address is sanctioned and can't receive assets"
        }
    }
    ```
  </Accordion>

  <Accordion title="recipient_person_sanctioned" icon="user-shield">
    **Error Message:** "User does not meet the verification requirements to proceed. Check the user's verification status via GET /users/\{userLocator}/identity-verification."

    **Description:** The recipient did not pass the verification checks required to proceed. This is returned for any blocking KYC outcome, including sanctions/PEP screening matches, watchlist hits, banned users, restricted geographies, or failed full/light KYC.

    **Resolution:** The payout cannot proceed. Inspect the recipient's verification status via [`GET /users/{userLocator}/identity-verification`](/api-reference/users/get-identity-verification) to determine the specific reason and any next steps.

    <Expandable title="How to test">
      In staging, if you create a user profile with the name "Sanctioned User Name", Crossmint will consider them sanctioned. You can use this for testing.

      To trigger this error, [create a user](/identity/register-user-data#step-2-register-user-data) with the following data:

      ```json theme={null}
      {
          "personalData": {
              "firstName": "Sanctioned User Name",
              // ... any value for other required fields
          }
      }
      ```

      Then attempt a payout to that user's wallet.
    </Expandable>

    ```jsonc theme={null}
    // Example error response
    {
        "error": {
            "reason": "recipient_person_sanctioned",
            "message": "User does not meet the verification requirements to proceed. Check the user's verification status via GET /users/{userLocator}/identity-verification."
        }
    }
    ```
  </Accordion>

  <Accordion title="recipient_type_unsupported" icon="circle-xmark">
    **Error Message:** "Regulated transfers can only be sent to Crossmint-managed wallets or external wallets linked to a Crossmint user."

    **Description:** The destination address is not a Crossmint-managed wallet and is not linked to a Crossmint user. Payouts support transfers to Crossmint user wallets and to external wallets that a Crossmint user has linked to their account.

    **Resolution:** Ensure the recipient has a Crossmint wallet, or link the external wallet address to the recipient's Crossmint user with the [Link External Wallet API](/api-reference/users/link-wallet) before initiating the payout. After linking, the recipient may also need to prove ownership by signing the verification challenge (see the `recipient_ownership_unverified` entry below):

    ```bash theme={null}
    curl --request PUT \
        --url https://staging.crossmint.com/api/2025-06-09/users/YOUR_USER_LOCATOR/linked-wallets/YOUR_WALLET_ADDRESS \
        --header 'X-API-KEY: YOUR_API_KEY' \
        --header 'Content-Type: application/json' \
        --data '{
            "chain": "base-sepolia"
        }'
    ```

    ```jsonc theme={null}
    // Example error response
    {
        "error": {
            "reason": "recipient_type_unsupported",
            "message": "Regulated transfers can only be sent to Crossmint-managed wallets or external wallets linked to a Crossmint user."
        }
    }
    ```
  </Accordion>

  <Accordion title="recipient_ownership_unverified" icon="signature">
    **Error Message:** "Recipient external wallet ownership has not been verified. The recipient must sign the wallet ownership challenge before receiving regulated transfers."

    **Description:** The destination address is an external wallet linked to a Crossmint user, but the user has not proven ownership of the wallet by signing the ownership verification challenge.

    **Resolution:** Have the recipient sign the `verificationChallenge` returned when the wallet was linked (or retrieve it anytime via the [Get Linked Wallet](/api-reference/users/get-linked-wallet) endpoint), then submit the signature as `proof` using the same [Link External Wallet API](/api-reference/users/link-wallet):

    ```bash theme={null}
    curl --request PUT \
        --url https://staging.crossmint.com/api/2025-06-09/users/YOUR_USER_LOCATOR/linked-wallets/YOUR_WALLET_ADDRESS \
        --header 'X-API-KEY: YOUR_API_KEY' \
        --header 'Content-Type: application/json' \
        --data '{
            "chain": "base-sepolia",
            "proof": "YOUR_SIGNATURE"
        }'
    ```

    Once the response shows `"ownership": { "verified": true }`, retry the payout. The verification flow is the same one used for onramp — see [Proof of Ownership](/onramp/guides/proof-of-ownership) for the full flow, including how to sign the challenge on EVM and Solana.

    ```jsonc theme={null}
    // Example error response
    {
        "error": {
            "reason": "recipient_ownership_unverified",
            "message": "Recipient external wallet ownership has not been verified. The recipient must sign the wallet ownership challenge before receiving regulated transfers."
        }
    }
    ```
  </Accordion>
</AccordionGroup>

## Error Handling Best Practices

When implementing payouts, follow these best practices for error handling:

<Steps>
  <Step title="Validate recipient before transfer">
    Before initiating a payout, verify that the recipient has completed their onboarding and provided all required personal data. This can help prevent `recipient_personal_data_missing` errors.
  </Step>

  <Step title="Implement retry logic for transient errors">
    Some errors may be transient (e.g., temporary API issues). Implement appropriate retry logic with exponential backoff for non-compliance errors.
  </Step>

  <Step title="Log compliance failures">
    Log all compliance-related errors for audit purposes. These logs are important for regulatory reporting and investigating failed transfers.
  </Step>

  <Step title="Notify users of compliance issues">
    When a payout fails due to compliance issues, notify the affected users with clear instructions on how to resolve the issue (e.g., completing their profile, contacting support).
  </Step>
</Steps>

## Supported Transfer Types

When calling the Transfer Token API, you must specify the `transactionType` field to indicate whether the transfer requires compliance checks:

| Type                 | Description                                                                                                                                                                                                                                   |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `regulated-transfer` | The transaction is routed through Crossmint's regulated infrastructure. This enables compliance with regulatory requirements and performs AML screening, sanctions checks, and travel rule validation. **Required for payouts to customers.** |
| `onramp`             | The transaction is routed through Crossmint's regulated infrastructure, similar to `regulated-transfer`. Used for fiat-to-crypto onramp flows that require compliance checks.                                                                 |
| `direct`             | Standard transfer with no compliance checks. Used for internal treasury-to-treasury transfers. This is the default if `transactionType` is omitted.                                                                                           |

<Warning>
  Omitting the `transactionType` field defaults to `direct`, which **skips all compliance checks**. Always set `transactionType` to `regulated-transfer` (or `onramp` for fiat-to-crypto flows) when sending payouts to customer wallets.
</Warning>

<Note>
  Compliant payouts work for transfers from **Crossmint treasury wallets** to **Crossmint user wallets** and to **external wallets linked to a Crossmint user** with verified ownership. Payouts to unlinked external wallets are not supported.
</Note>

## Additional Resources

<CardGroup cols={2}>
  <Card title="API Reference" icon="terminal" color="#B56710" href="/api-reference/wallets/transfer-token">
    Deep dive into the transfer API reference
  </Card>

  <Card title="Talk to an expert" icon="message" iconType="duotone" color="#ADD8E6" href="https://www.crossmint.com/contact/sales">
    Contact the Crossmint sales team for support
  </Card>
</CardGroup>
