---
title: "Create a transfer"
method: POST
path: "/v1/transfers"
tags: ["Treasury"]
---

# Create a transfer

`POST /v1/transfers`

## Request

- Base URL: `https://zernio.com/api`
- URL: `https://zernio.com/api/v1/transfers`
- Auth: HTTP bearer

Moves money from your balance to a payee.

### Fee

The payee receives the full `amountCents`. Zernio's fee (4%, 50 cent minimum) is added on top, so your balance is debited `totalCents` = `amountCents` + `feeCents`.

### Who can call it

Needs an owner, admin or billing admin, signed in or through a full-access key they created. A profile-scoped key answers 403.

### Errors

- 402: the balance does not cover the amount plus the fee.
- 402: the balance is negative after a dispute or refund of a card top-up ("Your balance is negative after a dispute or refund of a card top-up. Add funds to cover it before sending transfers.").
- 409: the payee has not finished onboarding, or the Idempotency-Key is still processing.
- 409 `payout_funds_locked`: money from a card top-up can be transferred from 7 days after the charge. The message names the amount and the day ("$100.00 from your card top-up on Oct 29 can be paid out from Nov 5.").

### Retries

A same-key retry never makes the transfer twice within Stripe's 24-hour idempotency window. It answers 409 while another request is still waiting on Stripe for it.

A transfer Stripe has not confirmed after 24 hours answers 409 until the daily reconciliation marks it paid (Stripe made it) or failed (it did not).

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `payeeId` string, required: 24-character payee id
  - `amountCents` integer, required: Amount the payee receives, in cents. The fee is added on top.
  - `reference` object
    - `type` string, required
    - `id` string, required

## Response `201`

Transfer created

- TreasuryTransfer
  - `id` string
  - `payeeId` string: 24-character payee id
  - `payeeName` string, nullable: The payee's name when Zernio knows it, for example a creator's display name in one of your creator campaigns; null otherwise
  - `destinationType` 'creator' | 'ad_account'
  - `amountCents` integer: Amount the payee receives, in full
  - `feeCents` integer: Zernio fee added on top, 4% with a 50 cent minimum
  - `totalCents` integer: Amount debited from your balance: amountCents plus feeCents
  - `currency` string
  - `reference` object
    - `type` string
    - `id` string
    - `name` string, nullable: What the transfer paid for, when Zernio knows it: the campaign name for a creator campaign payout; null otherwise
  - `status` 'pending' | 'paid' | 'failed' | 'reversed'
  - `failureReason` string, nullable
  - `createdAt` string, date-time

## Other responses

- `400`: Invalid request
- `402`: Payment method or enterprise contract required: the authenticated account hit a billing gate before the connection could proceed. Switch on `reason` to render the right prompt: - `free_tier_exceeded`: the team has connected more accounts than the free tier allows. Add a payment method to continue (billed per additional connected account). - `twitter_passthrough`: connecting an X account requires a card on file from day one, because X API calls incur real per-call pass-through costs. Applies to the 1st X account, not only the 3rd+. - `enterprise_required`: the team's enterprise contract sets a connected-account cap and it has been reached. Self-service teams have NO cap (the $1/account rate continues at any scale). The user already has a card on file; this gate is about contract terms. - `card_verification_required`: the card on file was flagged at add time (prepaid, or issued in a different country than the billing address). A one-time verification charge of `details.verification_amount_cents` (USD) is pending; it is credited to the team's usage balance, not a fee. **What to do:** redirect the end-user to `dashboard_url`. - `free_tier_exceeded`, `twitter_passthrough`, `card_verification_required`: it opens the Zernio billing page (the add-payment-method drawer, or the verification step). The request succeeds once the card is on file (or verified, or paid). - `enterprise_required`: it opens the enterprise contact form (not the billing tab), to adjust the contract's limit.
- `403`: Refused (`insufficient_permissions`). One of: - A restricted API key without the `money` resource group. - A profile-scoped API key. Money leaves the whole team's balance, so moving it needs a full-access key or a signed-in session ("Moving money needs a full-access key or a signed-in session."). - A member or viewer, signed in or through a key they created. Only an owner, admin or billing admin moves money.
- `404`: Resource not found
- `409`: Payee has not finished payout onboarding, the Idempotency-Key is still processing, another request is waiting on Stripe for this transfer, or Stripe has not confirmed it within 24 hours
- `422`: Idempotency-Key reused with a different request

## Changes

- **2026-10-28** `8f2c44dd7550`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/transfers/post.md)

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) - [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/c319eb5d95b6?raw)
