# Payments

> Paying creators, retainers, balances and withdrawals.

Source: https://www.onlucra.com/docs/api-payments

Brands pay creators through Lucra. As soon as the brand's payment goes through, the creator's share goes to their own Stripe balance. It stays there until they withdraw it to their bank, whenever they choose (instantly, for a fee).

Amounts are in cents. On money objects, `amount` is the gross, `fee` comes out of it, and `net` is what's left. Every write that moves money needs an `Idempotency-Key`.

## Fees

These are the standard plan's defaults. An account's plan or agreed terms can differ; **Settings → Billing** shows its own.

| Fee                   | Default        | Paid by   |
| --------------------- | -------------- | --------- |
| Payment to a creator  | 1.50% of the payment | The brand |
| Lucra fee             | 1.25% of the first $100,000 a month, 1% up to $300,000, 0.75% up to $1,000,000, 0.5% above, of ad spend, or of attributed sales for revenue-share programs          | The brand |
| Retainer pay          | 1.50% of the payment | The brand |
| Standard withdrawal   | Free           |           |
| Instant withdrawal    | 1.5% (min 50¢) | Creator   |
| Payout converted from USD | 1%         | Creator   |

## Pay a creator

Send the creator and the `net` they should receive. The brand is charged that plus Lucra's 1.50% (`fee`), so the creator gets the full `net`:

```ts
await lucra.payments.create({
  body: { creator: "crtr_026UYfiw1Y6OvUpuKWiOpeL", net: 50000, note: "Launch bonus" },
  headers: { "Idempotency-Key": "launch-bonus-crtr_026UYfiw1Y6OvUpuKWiOpeL" },
})
```

The payment is `pending` until Stripe confirms the charge, then `succeeded`, or `failed` with a `payment.failed` webhook. A retry with the same `Idempotency-Key` returns the same payment and never charges twice.

A payment waiting on the brand (`pending_approval`) is released with `PATCH /v1/payments/:id` and `{ "status": "approved" }`. Until it's sent to the creator (within minutes of the charge), the brand can dispute a payment with `{ "status": "disputed", "reason": "…" }`; once sent, the payment is final.

## Pay

One `pay` object describes how programs and retainers pay. This one guarantees $500 a month, plus $8 per 1,000 views and $50 per approved video, up to $2,000:

```json
{
  "base": 50000,
  "rules": [
    { "per": "view", "amount": 800, "unit": 1000 },
    { "on": "approved_submission", "amount": 5000 }
  ],
  "cap": 200000,
  "schedule": { "every": "month" }
}
```

- `base` is guaranteed each period. Programs don't take one.
- `rules` pay per result (`per`), per event (`on`), a percent of ad spend or revenue (`percentOf`), or per [conversion](https://www.onlucra.com/docs/api-attribution#pay-per-conversion) credited to the creator (`conversion`, with an `amount` or a `percent` of its value).
- `cap` is the most paid per period.
- `schedule` says when it pays: `{ "every": "week" }` (or `day`, `month`, `quarter`, `event`), or `{ "after": "measurement", "days": 7 }` for organic programs.
- `approval: "manual"` holds each period's payment for you to approve (retainers only).

### Pay that can't be paid

Pay is checked when it's saved. If it couldn't be paid as set, you get `422 invalid_pay` listing every problem:

| `code`                    | When                                                                                                                                                                                                                                                                          |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pay_invalid_value`       | An `amount` under 1 cent, a `percent` not above 0 and at most 100, or a `unit` under 1.                                                                                                                                                                                       |
| `pay_cap_zero`            | `cap` is 0. Send `null` for no cap.                                                                                                                                                                                                                                           |
| `pay_cap_below_amount`    | `cap` is below an amount paid in one go (an `on` rule's amount, a `per` rule's amount for one unit, or `base`), so it could never be paid.                                                                                                                                    |
| `pay_cap_required`        | An organic program without a `cap`.                                                                                                                                                                                                                                           |
| `pay_base_needs_period`   | A `base` on a program, or with an `event` or `measurement` schedule. A base needs a schedule of every `day`, `week`, `month`, or `quarter`.                                                                                                                                   |
| `pay_schedule_mismatch`   | A retainer not paid every `day`, `week`, `month`, or `quarter`; a program on another schedule than its type's; or an organic program paying on ad results (ad spend, attributed revenue, impressions, clicks, installs, leads, purchases), which organic programs never have. |
| `pay_duplicate_rule`      | Two rules on the same results (the same metric and `scope`), which would pay them twice; combine them into one.                                                                                                                                                               |
| `pay_scope_invalid`       | A `scope.campaign` that isn't one of the brand's campaigns, or for a program, not one built from the program's work. A new program has none yet.                                                                                                                              |
| `pay_exceeds_budget`      | A single amount larger than the program's `budget.amount`.                                                                                                                                                                                                                    |
| `pay_primary_missing`     | A program without the rule its type pays per result (see [programs](https://www.onlucra.com/docs/api-programs#pay)).                                                                                                                                                                            |
| `pay_primary_changed`     | Program pay, or a creator's pay, that changes what the program pays per result on (for example ad spend to leads). Change the amount instead.                                                                                                                                 |
| `pay_program_unsupported` | `"approval": "manual"` on a program, or pay per approved video on an organic program.                                                                                                                                                                                         |

## Retainers

A retainer is recurring pay for one creator. Offer one with `POST /v1/retainers` and `{ "creator", "pay", "deliverables" }`, with an `Idempotency-Key`. A retainer can also pay only on conversions, like an affiliate deal: `{ "rules": [{ "conversion": "conv_1fK7pQ2wXy9LmN4bVc8RtZa", "amount": 500 }], "schedule": { "every": "week" } }`.

The creator accepts or rejects it with `PATCH /v1/retainers/:id` and `{ "status": "accepted" }` (or `"rejected"`). A partner answers for its roster creator with `Lucra-Account: crtr_…`.

Each period pays its `base` up front and earned results at the end. Cancel with `{ "status": "canceling" }`; the current period is still paid.

### Failed charges

A declined charge retries after 1, 2, 2, and 2 days, then the retainer goes `past_due` until the brand updates its payment method. Program pay retries the same way.

## Balance

`GET /v1/balance` shows a creator's earnings, or a partner's commissions ready to withdraw. Brands keep no balance: each charge goes to their saved card or bank.

## Payouts

Every few minutes, Lucra sends each paid-for earning to the creator's Stripe balance, against the brand's charge, once they've verified their identity. `GET /v1/payouts` lists them. Nothing is paid out to a bank on its own: the balance stays until its owner withdraws it.

## Supported payout countries

Payouts run on one of two rails, set by the payout country when the creator first sets up payouts.

- **Stripe Connect**: Austria, Belgium, Bulgaria, Canada, Croatia, Cyprus, Czechia, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Iceland, Ireland, Italy, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Netherlands, Norway, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, Switzerland, United Kingdom, United States. Earnings go to the creator's (or partner's) Stripe balance, which they withdraw whenever they choose.
- **Stripe Global Payouts**: Australia, India, Indonesia, Israel, Japan, Jamaica, Mexico, Monaco, Morocco, New Zealand, San Marino, Singapore, Trinidad and Tobago, for creators only. Lucra keeps the earnings and pays them to the creator's bank every Monday in one payout, less the cross-border fee. They finish setup on Stripe's own page (the `url` from `POST /v1/connections`, even with `checkout: elements`), there's no withdrawing in between, and `GET /v1/withdrawals` lists the weekly payouts.

Global Payouts countries appear only once Lucra switches them on. `GET /v1/payouts/countries` lists the countries open now. A country that isn't open is a 422 (`payout_country_unavailable`). The `country` enum on `POST /v1/connections` (provider `stripe`) lists every country on both rails. Creators anywhere else can't be paid out yet.

## Withdrawals

`POST /v1/withdrawals` moves a balance to the bank anytime, with `speed` set to `standard` (free) or `instant`. Leave out `amount` to withdraw everything. A creator adds their bank account on their first withdrawal. Global Payouts creators are paid every Monday instead, so their withdrawals return `weekly_payouts_only`.

## Earnings

`GET /v1/earnings` lists what a creator earned, with its status: `pending` (until the brand's payment goes through), `available`, `paid`, or `held` while a dispute is reviewed.

## Endpoints

- `GET /v1/balance` (read, SDK `lucra.balance.retrieve`): Get the balance: the account's commissions, or a creator's earnings
- `GET /v1/balance/tax` (read, SDK `lucra.balance.tax.retrieve`): Get US tax reporting: whether a taxpayer ID is on file, and what Lucra paid each year
- `GET /v1/balance/transactions` (read, SDK `lucra.balance.transactions.list`): List every ledger line on the account
- `POST /v1/payments` (write, SDK `lucra.payments.create`): Pay a creator
- `GET /v1/payments` (read, SDK `lucra.payments.list`): List payments to creators
- `GET /v1/payments/:id` (read, SDK `lucra.payments.retrieve`): Get a payment to a creator
- `PATCH /v1/payments/:id` (write, SDK `lucra.payments.update`): Approve or dispute a payment to a creator
- `GET /v1/payouts/countries` (read, SDK `lucra.payouts.countries.retrieve`): The payout countries that can be chosen now: a creator's (Stripe Global Payouts' too while it's on), or this brand's or partner's
- `GET /v1/payouts` (read, SDK `lucra.payouts.list`): List payouts: to creators from this brand's earnings, received by this account, or (as a creator) the creator's own
- `GET /v1/payouts/:id` (read, SDK `lucra.payouts.retrieve`): Get a payout
- `GET /v1/earnings` (read, SDK `lucra.earnings.list`): List a creator's earnings
- `GET /v1/earnings/:id` (read, SDK `lucra.earnings.retrieve`): Get one of a creator's earnings
- `POST /v1/withdrawals` (write, SDK `lucra.withdrawals.create`): Withdraw from the balance to the bank account or debit card (not for Global Payouts creators, paid every Monday)
- `GET /v1/withdrawals` (read, SDK `lucra.withdrawals.list`): List withdrawals from the balance, or a Global Payouts creator's weekly payouts
- `GET /v1/withdrawals/:id` (read, SDK `lucra.withdrawals.retrieve`): Get a withdrawal
- `GET /v1/retainers` (read, SDK `lucra.retainers.list`): List retainers
- `GET /v1/retainers/:id` (read, SDK `lucra.retainers.retrieve`): Get a retainer
- `POST /v1/retainers` (write, SDK `lucra.retainers.create`): Offer a creator a retainer
- `PATCH /v1/retainers/:id` (write, SDK `lucra.retainers.update`): Cancel a retainer, or, as the creator, accept or reject an offer or cancel
