> ## Documentation Index
> Fetch the complete documentation index at: https://glide-9da73dea.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Payouts

> Push 2 to 50 USD payouts in one atomic batch from your prefunded Glide treasury, with idempotency keys, per-item execution ids and exact replay semantics.

`POST https://glide.co/api/partner/treasury/transfers/batch`

One call moves USD from your prefunded Glide treasury into your users' personal Glide balances. The whole batch is a single synchronous database transaction. There is no queue, no provider call inside the transaction, and no partial success.

## The treasury model

Glide binds your partner record to exactly one business entity, one accounting owner, one entity-scoped USD balance, one external payer UUID and one reserve account. That binding is immutable once made. It does not create a business, approve it, or supply funds.

You prefund the treasury by settling real USD into the bound reserve account, then a Glide operator records the settlement against evidence. Funding is an operator attestation of already-settled external money, not a deposit API you can call.

Settlement through this endpoint means **spendable personal Glide USD credit**. It does not mean a bank withdrawal or an on-chain transfer. Both sides of each payout are completed transfer records with zero fee: a debit on your treasury and a credit on the user. The user's copy shows as incoming and is labelled **Payout from partner** in their transaction history.

## Requirements

| Requirement                                                          | Where it comes from                                 |
| -------------------------------------------------------------------- | --------------------------------------------------- |
| `treasury:write` on your token                                       | A Glide admin enables it on your token entitlements |
| An active treasury with an unexpired mandate                         | Glide binds, funds and activates it                 |
| A pre-creation receipt for each recipient                            | Your own `POST /api/partner/users` call             |
| `payouts:receive` **and** `transfers:read` on each recipient's grant | The user's consent screen                           |
| Each recipient's account is active                                   | The user                                            |

This endpoint adds no verification, wallet, receiving-account or webhook gate of its own. A recipient with no verification can still be paid. Your token does not also need the read or receive scopes to call this endpoint, though you do need them to read the results back.

## Request

Headers:

| Header            | Value                                                |
| ----------------- | ---------------------------------------------------- |
| `Authorization`   | `Bearer <token>` carrying `treasury:write`           |
| `Content-Type`    | `application/json`                                   |
| `Idempotency-Key` | 1 to 128 characters from `[A-Za-z0-9._:-]`, required |

```json theme={null}
{
  "payer_entity_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "currency": "USD",
  "items": [
    { "execution_id": "11111111-1111-4111-8111-111111111111", "payout_id": "22222222-2222-4222-8222-222222222222", "user_id": "33333333-3333-4333-8333-333333333333", "amount_cents": "2500", "reference": "invoice-104" },
    { "execution_id": "44444444-4444-4444-8444-444444444444", "payout_id": "55555555-5555-4555-8555-555555555555", "user_id": "66666666-6666-4666-8666-666666666666", "amount_cents": "4000", "reference": "invoice-105" }
  ]
}
```

| Field                  | Rule                                                                                |
| ---------------------- | ----------------------------------------------------------------------------------- |
| `payer_entity_id`      | UUID. Must equal your binding's external payer id. It cannot select another balance |
| `currency`             | Exactly `"USD"`                                                                     |
| `items`                | 2 to 50 entries. Every object is strict and rejects unknown fields                  |
| `items[].execution_id` | UUID, unique within the request. Your idempotency unit for one payment attempt      |
| `items[].payout_id`    | UUID, unique within the request. Your obligation identifier                         |
| `items[].user_id`      | UUID of the recipient                                                               |
| `items[].amount_cents` | Positive integer string. No sign, decimals, exponent, whitespace or leading zero    |
| `items[].reference`    | 1 to 200 characters                                                                 |

UUIDs are canonicalized to lowercase. Individual amounts and the request total must each be at most `9007199254740991`. The streamed body limit is 64 KiB, which comfortably fits 50 items.

The same user may appear more than once in a batch, as long as each entry is a distinct obligation with its own `execution_id` and `payout_id`.

### Choosing ids

`execution_id` identifies one attempt to move a specific amount to a specific user. Generate it once, store it next to the obligation in your database, and reuse it on every retry. `payout_id` identifies the obligation itself. Neither is generated by Glide, and neither should be regenerated because a call failed.

<Warning>
  Never mint a fresh `execution_id` to "retry" a payment. A new id is a new payment. Retrying with the ids you already stored is what makes a lost response safe.
</Warning>

## Response

HTTP 200, including on replay, with one result per requested item **in request order**.

```json theme={null}
{
  "batch_id": "77777777-7777-4777-8777-777777777777",
  "treasury_entity_id": "88888888-8888-4888-8888-888888888888",
  "currency": "USD",
  "total_amount_cents": "6500",
  "items": [
    { "execution_id": "11111111-1111-4111-8111-111111111111", "user_id": "33333333-3333-4333-8333-333333333333", "transfer_id": "99999999-9999-4999-8999-999999999999", "amount_cents": "2500", "state": "processed", "version": 1, "fee_cents": "0", "fee_included": false },
    { "execution_id": "44444444-4444-4444-8444-444444444444", "user_id": "66666666-6666-4666-8666-666666666666", "transfer_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", "amount_cents": "4000", "state": "processed", "version": 1, "fee_cents": "0", "fee_included": false }
  ]
}
```

| Field                | Notes                                                                           |
| -------------------- | ------------------------------------------------------------------------------- |
| `transfer_id`        | The user's credit transaction id. Store it against your payout                  |
| `state`              | Always `processed`. There is no partial or pending state                        |
| `version`            | Always `1`                                                                      |
| `fee_cents`          | Always `"0"`                                                                    |
| `fee_included`       | Always `false`                                                                  |
| `total_amount_cents` | Includes reused items, so it can exceed the money actually debited by this call |

`transfer_id` is the correlation key for everything downstream. It appears in `GET /api/partner/users/:id/transfers` and in the `transfer.settled` webhook.

## Atomicity

The executor locks your partner mapping, treasury, relationship receipts, users and grant rows, validates the entity and balance binding, then credits recipients in a deterministic order. A revocation, suspension or configuration change either lands before execution or waits until the already-authorized transaction commits.

The success audit, batch response, receipts, treasury debit, user credits, paired transactions, activity entry and webhook outbox all commit together. If the audit or the outbox fails, the entire new batch rolls back. You will never receive a successful subset.

## Caps and policy

Under the treasury lock, Glide enforces the caps an operator configured with you:

| Cap                    | Applies to                           |
| ---------------------- | ------------------------------------ |
| Per item               | A single item's amount               |
| Per batch              | The sum of new items in this request |
| Rolling 24 hours       | New amount moved in the trailing day |
| Lifetime               | New amount moved since binding       |
| Transfers per hour     | Count of new transfers               |
| Transfers per 24 hours | Count of new transfers               |

Only **new** transfers consume amount or count allowance, so replays are free. Hitting a cap exactly is allowed. A cap error names the limit it violated without revealing how much allowance you have left.

Caps must increase from per item through per batch and rolling 24 hours to lifetime. If a step-up amount is configured and an item exceeds it, this release denies the batch with `policy_denied`. There is no approval token that bypasses it. Ask Glide to raise the cap instead.

New executions additionally require an active treasury, an unexpired mandate, accepted owner membership in the active business entity, and verified funding. The debit itself is guarded by `available_balance >= new_total`, so an underfunded batch fails rather than overdrawing.

## Idempotency and replay

| Situation                                                                                         | Result                                                                             |
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Same key and canonical request                                                                    | Original stored response. No new money, receipt, usage or event                    |
| Same key and a changed request, including reordered items                                         | 409 `idempotency_conflict`                                                         |
| Different key, identical previously executed item                                                 | Reuses that item's `transfer_id`. Only unseen executions are debited               |
| Reused `execution_id` with a changed recipient, payer, `payout_id`, amount, currency or reference | 409 `execution_conflict`. No item in the batch executes                            |
| Validation, policy or funds rejection                                                             | Rejection is audited. No settled batch is stored and no execution ids are consumed |
| Commit succeeded but you lost the response                                                        | Retry the same key and payload to recover the original body                        |
| Timeout or any uncertain outcome                                                                  | Keep the execution ids and retry. Do not manufacture replacements                  |

<Note>
  An unconfirmed call is not a failed call. On a timeout, a dropped connection, a 5xx or an unparseable response, resend the identical body with the identical `Idempotency-Key`. A replayed 200 reconciles you against the transfers that already exist.
</Note>

Replay still requires current partner authentication, receipt membership, active users and both current consents. Treasury suspension or an expired mandate stops **new** money movement while leaving an authorized replay available. A batch made entirely of reused items needs no new mandate and consumes no policy allowance.

Revoking a user's access can stop you reading a historical receipt. It does not reverse the payment. A later failure of the user's own bank withdrawal cannot change your completed treasury transfer either.

## Errors

Rate limit is 10 requests per minute per partner, with `Retry-After: 60` on a 429.

```json theme={null}
{ "error": { "code": "insufficient_funds", "message": "Treasury balance is insufficient for this batch", "request_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc" } }
```

| HTTP | Codes                                                                                                            |
| ---- | ---------------------------------------------------------------------------------------------------------------- |
| 400  | `invalid_request`, `invalid_json`                                                                                |
| 401  | `invalid_token`, with `WWW-Authenticate: Bearer`                                                                 |
| 403  | `grant_required`, `partner_inactive`, `user_inactive`, `treasury_inactive`, `payer_not_allowed`, `policy_denied` |
| 404  | `user_not_found`, `treasury_not_configured`                                                                      |
| 409  | `idempotency_conflict`, `execution_conflict`, `insufficient_funds`                                               |
| 413  | `body_too_large`                                                                                                 |
| 429  | `rate_limited`                                                                                                   |
| 500  | `internal_error`                                                                                                 |
| 503  | `auth_unavailable`, `audit_unavailable`                                                                          |

See [Errors](/partners/errors) for what to do about each one.

## After settlement

The user's credit lands in their personal Glide USD balance immediately, visible under **Glide balance** in the Glide app. From there the user, not you, drives the cash-out:

1. Convert USD to USDC in Glide, reviewing the quoted fees. **This step requires approved verification.**
2. Continue to a bank withdrawal from the conversion receipt, or open the bank flow directly against available USDC.

Held balances are unchanged by your payout. A withdrawal that later fails restores the user's own funds and never touches your completed payout receipt.

## Treasury operations

These are Glide-side operations. Ask Glide to perform them; they are not partner-callable.

| Ask Glide to               | What happens                                                                                                                                                                                         |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bind your treasury         | Supplies your entity, external payer id, reserve account, caps and mandate expiry, and creates an empty USD balance in `disabled` status                                                             |
| Configure policy           | Sets per-item, per-batch, rolling 24 hour and lifetime caps, transfer counts, mandate expiry and an optional step-up amount. Each edit bumps a policy version and audits the before and after values |
| Record funding             | Attests settled external USD against evidence, credits the balance and writes an immutable funding receipt                                                                                           |
| Set treasury status        | `active`, `suspended` or `disabled`. Activation requires a valid binding, a future mandate, an active partner and verified funding                                                                   |
| Report on the treasury     | Returns configuration, available and held cents, rolling and lifetime usage, and recent funding receipts                                                                                             |
| List batches and transfers | Pages your batch history and per-batch items, including reused executions                                                                                                                            |

Binding cannot silently adopt existing funds, and funding receipts are immutable. Corrections need an explicit accounting operation rather than an edit. Treasury suspension leaves your reads and webhook delivery working and only stops new payouts. Partner suspension stops everything.
