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

# Automate payments

> Send payouts from a treasury wallet under policy, with a service key that can do nothing else.

Payouts (supplier settlements, customer withdrawals, partner disbursements) are the highest-risk operation a payments business automates. This solution runs them from your backend with an API key that can only request withdrawals from one wallet, inside limits and whitelists the platform enforces, with human approval above a threshold.

## What you'll need

* A treasury wallet with the payout asset attached and funded.
* Owner or admin access to the console to set policies and create the service key.
* The list of destination addresses you pay, if you intend to whitelist them.

## Components to configure

| Component              | Role                                                                                                                                        |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| **Service API key**    | Scoped to `withdrawals:create` and `withdrawals:read`, bound to the treasury wallet, restricted to your backend's IP range, with an expiry. |
| **Address whitelist**  | Only known destinations can receive funds; new entries wait out a timelock before they are usable.                                          |
| **Spend limits**       | A per-transaction ceiling and a daily ceiling, evaluated on every request.                                                                  |
| **Approval threshold** | Payouts above an amount are held for an approval group.                                                                                     |
| **Webhooks**           | `withdrawal.*` events drive your payout state machine.                                                                                      |
| **Withdrawal freeze**  | The emergency stop that fails every payout closed.                                                                                          |

## Steps

<Steps>
  <Step title="Set the policy in the console">
    Under **Account Settings → Security & Approvals**, enable **Only allow whitelisted addresses** and add your destinations to the **Address Book**; set **Per transaction** and **Per day** limits under **Spend limit**; and enable **Require approval to withdraw** with a **Require above amount** threshold and the approval group that must sign off. Set **On balance drift** to the behaviour you want if the on-chain balance disagrees with the ledger.
  </Step>

  <Step title="Create the service key">
    In **Developer**, create an API key with scopes `withdrawals:create` and `withdrawals:read`, restrict it to the treasury wallet, allowlist your backend's IP range and set an expiry. Store the secret in your secrets manager; it is shown once.
  </Step>

  <Step title="Request each payout with an idempotency key">
    `POST /api/v1/wallets/{wallet_id}/request-withdrawal` with `asset_id`, `amount` and `recipient_address`, and an `X-Idempotency-Key` equal to your payout's own identifier. A retry after a timeout returns the original result instead of paying twice. Rejections come back immediately with a reason: destination not whitelisted, limit exceeded, withdrawals frozen (`423`).
  </Step>

  <Step title="Drive your state machine from events">
    Subscribe to `withdrawal.created`, `withdrawal.pending_approval`, `withdrawal.submitted`, `withdrawal.confirmed`, `withdrawal.completed`, `withdrawal.failed` and `withdrawal.cancelled`. Treat `confirmed` as settled. Reconcile with `GET /api/v1/withdrawals/{withdrawal_id}`, which carries the transaction hash and block once broadcast.
  </Step>

  <Step title="Rehearse the emergency stop">
    Confirm that an owner can reach **Security & Approvals → Emergency → Freeze withdrawals** quickly, and that your backend handles the `423` that follows without retrying in a loop. Unfreeze is a deliberate, audited action.
  </Step>
</Steps>

## Considerations

* Amounts are strings in the asset's units; never send floating-point numbers.
* A payout that needs approval stays `pending` until an approver acts in the console. Design for that latency: hold the customer-facing status at "processing" rather than failing.
* Daily limits reset on the platform's day boundary; size them to your real volume plus headroom, not to the maximum you could imagine paying.
* Stablecoin payouts on EVM networks are token transfers from the same wallet and address as the native asset.

## Related

* [Define treasury policies](/solutions/compliance/define-treasury-policies): the full set of limits and destination rules.
* [Build programmable approval policies](/solutions/compliance/build-programmable-approval-policies): designing the approval tier.
* [Transactions & signing](/wallets/transactions-and-signing): every state a withdrawal passes through.
