---
title: "Record a payout"
method: POST
path: "/v1/projects/{project}/referral-payouts"
tags: ["Referral Program"]
---

# Record a payout

`POST /v1/projects/{project}/referral-payouts`

## Request

- Base URL: `https://api.subscriby.net`
- URL: `https://api.subscriby.net/v1/projects/{project}/referral-payouts`
- Auth: HTTP bearer

```bash
curl -X POST https://api.subscriby.net/v1/projects/7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13/referral-payouts \
  -H "Authorization: Bearer sbt_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "affiliate_id": "0b1c2d3e-4f50-4617-8a9b-0c1d2e3f4a5b",
    "amount": 30,
    "currency_id": "f0e1d2c3-b4a5-4697-8877-665544332211",
    "reference": "PAYPAL-7F3D1C92"
  }'
```

Records money that already left: you paid the affiliate outside Subscriby and file it here, so the ledger marks their oldest payable commissions in that currency as paid, first in first out, until the amount is covered. Emits `referral.payout_recorded`. Answers `201` with the payout.

> **The amount is capped at the payable balance.** Read the affiliate's `balances` first; `payable` in the currency is the most a payout may record. Pending commissions are still inside the hold and cannot be paid yet.

## Path parameters

- `project` string, uuid, required

## Headers

- `Idempotency-Key` string, uuid, required

## Request body

- StoreReferralPayoutRequest: A payout to record: the affiliate, how much was paid, in which currency, and your own reference and note.
  - `affiliate_id` string, uuid, required: The affiliate who was paid, as the referral-affiliates endpoints list them.
  - `amount` number, required: What was paid, greater than 0, at most the affiliate's payable balance in that currency.
  - `currency_id` string, required: The currency the commissions were earned in; a balance exists per currency.
  - `reference` string, nullable: Your own reference, such as a transfer id; at most 120 characters.
  - `note` string, nullable: A note for your records; at most 1,000 characters.

## Response `201`

The payout.

- object
  - `data` ReferralPayoutResource, required: A payout recorded against an affiliate: how much the creator paid them outside Subscriby, in which currency, and how many payable commissions it covered.
    - `id` string, required: The payout's id.
    - `project_id` string, required: The project the programme belongs to.
    - `affiliate_id` string, required: The affiliate who was paid.
    - `amount` string, required: What was paid, as a decimal string with four places.
    - `currency_id` string, required: The currency it was paid in.
    - `currency` string: That currency's ISO 4217 code, present when the relation is loaded.
    - `reference` string, nullable, required: The creator's own reference, such as a transfer id; null for none.
    - `note` string, nullable, required: The creator's note; null for none.
    - `paid_at` string, required: When the payout was recorded, ISO 8601.
    - `recorded_by_user_id` string, nullable, required: The creator or teammate who recorded it.
    - `rewards_paid` integer: How many commissions the payout covered, present when the relation is loaded.
    - `created_at` string, nullable, required: When the row was written, ISO 8601.

## Other responses

- `400`: `IDEMPOTENCY_KEY_MISSING`: every write needs an `Idempotency-Key` header. Send a fresh UUID per distinct operation.
- `401`: `AUTHENTICATION_REQUIRED`: the request carries no bearer token, or one that is revoked, malformed, or minted for another environment (an `sbt_test_` token on production).
- `403`: `TOKEN_MISSING_ABILITY`: the token is valid but does not carry the ability this endpoint requires; `error.context.required_ability` names the one to grant. An endpoint that also checks who owns a row or which tier the account is on answers `FORBIDDEN`, `TEAM_TIER_REQUIRED` or `CONNECTOR_TIER_REQUIRED` with the same status, and says so in its own description.
- `404`: `RESOURCE_NOT_FOUND`: an id in the path names nothing the token can see. `TENANT_MISMATCH`: the project sits outside the token's `scope:project:` allow-list, or the token carries no team scope. Both answer 404 rather than 403 so that existence outside the token's scope cannot be inferred.
- `409`: `IDEMPOTENCY_KEY_REUSED`: the key was already used in the last 24 hours with a different request body.
- `422`: `VALIDATION_FAILED`: the payload broke a rule, and `error.fields` maps each offending key to its messages. A refusal from the domain, such as a plan that cannot go on sale or a member who cannot be removed, uses the same code with `error.message` saying why and no `fields`. On this endpoint: `VALIDATION_FAILED`: when the affiliate is not one of the project's, the currency is unknown, or `amount` exceeds the payable balance in that currency; `error.context.balance` carries what is payable.
- `425`: `IDEMPOTENCY_REPLAY_IN_PROGRESS`: the first request with this key is still running; retry in a few seconds and the original response is replayed.
- `429`: `RATE_LIMITED`: the token has spent its 300 requests a minute or 10,000 an hour; `Retry-After` says when the next one is accepted.

---

[API](https://skmtc.dev/memberpass/apis/subscriby-rest-api.md) · [All operations](https://skmtc.dev/memberpass/apis/subscriby-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/memberpass/apis/subscriby-rest-api/revisions/59c4df91d764?raw)
