TANITARDevelopers
GuidesRewards

Payouts

Pay cash prizes from your own Stripe Connect account to your players' connected accounts, without Tanitar ever holding the funds.

A payout sends the cash value of a prize award to the player who won it. The money moves from your Stripe Connect Standard account to the player's connected Stripe account, as a Stripe transfer made on your account's behalf. Tanitar never holds, pools or forwards the funds: it decides when a payout is due, submits it once, and records what Stripe reports.

Before you start

Payout configuration is done in the dashboard by an administrator of your organization, not with an API key. See Payouts in the dashboard. There you:

  1. Connect your Stripe account (or create one) and complete Stripe's onboarding.
  2. Choose the currencies you allow for payouts. A payout is made only in your account's default currency, and only if it is on that list.
  3. Register each winning player's connected Stripe account (acct_…) as a payout recipient. A recipient must be eligible to be paid.

Then, on the API side:

  • Check that payouts are available: GET /v1/payouts/capabilities returns { "providers": ["stripe"] } when the deployment's Stripe preflight passed, and { "providers": [] } otherwise.
  • Create a prize award definition with a payout of currency (three lowercase letters, such as eur) and amountMinor (an integer in the currency's minor unit), bound to a tournament and a rank. See Award kinds and awards.

When the tournament completes, the winner receives the prize award automatically.

Request a payout

POST /v1/awards/:awardId/payouts with scope rewards:mutate and no body.

curl -X POST "$REWARD_API_URL/v1/awards/awd_91c2/payouts" \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

There is exactly one payout per award. Requesting it again, with any key, returns the same record.

The request is checked before anything reaches Stripe:

AnswerCause
503 PAYOUT_DISABLEDPayouts are not enabled on this deployment
404 AWARD_NOT_FOUNDNo such award in your tenant
422 PAYOUT_KIND_UNSUPPORTEDThe award is not an applied prize held by a player, or its definition has no payout
422 PAYOUT_AMOUNT_INVALIDThe definition's amount is not a positive integer
503 PAYOUT_CONFIGURATION_UNAVAILABLEYour payout configuration could not be read; nothing was recorded, retry later

If the request is admissible but your configuration does not allow it, the payout is recorded as rejected with a failureCode, and Stripe is never called:

failureCodeCause
PAYOUT_ACCOUNT_INACTIVEYour Stripe account is not connected, onboarded and active
PAYOUT_RECIPIENT_INELIGIBLEThe player has no eligible connected account registered
PAYOUT_CURRENCY_UNSUPPORTEDThe prize currency is not your account's default currency or not on your allowlist

Otherwise the payout is claimed and Tanitar submits it in the background.

Statuses

StatusMeaning
claimedAccepted and waiting for its next attempt
submittedSent to Stripe under a stored idempotency key; the answer is not yet recorded
settledStripe made the transfer; transferId is Stripe's transfer id
reversedThe transfer was settled and later reversed, fully or partly (amountReversed)
failedEvery attempt failed, or Stripe refused the transfer
rejectedRefused before any call to Stripe (see failureCode)
unresolvedStripe's answer was lost; the next reconcile finds out what happened

A payout is attempted up to six times, on a fixed ladder: immediately, then after 30 seconds, 2 minutes, 10 minutes, 30 minutes and 2 hours. Each attempt reuses a key stored before the call, so Stripe never executes the same attempt twice, and an attempt whose answer was lost is checked with Stripe before any new one is made.

Read and reconcile

RouteScopeReturns
GET /v1/payoutsrewards:readEvery payout in your tenant
GET /v1/awards/:awardId/payoutsrewards:readThe payout of one award
POST /v1/payouts/:payoutId/reconcilerewards:mutateThe payout after asking Stripe for its current state

Neither list is paginated or filtered. Each payout reads as:

{
  "payoutId": "pay_5d0e…",
  "awardId": "awd_91c2",
  "status": "settled",
  "currency": "eur",
  "amountMinor": 5000,
  "transferId": "tr_1Q…",
  "settledAt": "2026-09-30T12:40:03.000Z",
  "playerId": "player-7f3a",
  "failureCode": null,
  "amountReversed": 0,
  "attemptCount": 1
}

Reconcile reads the transfer from Stripe and records it: a transfer found settles the payout (or marks it reversed), and a transfer proven absent lets the next attempt proceed. If Stripe cannot be reached, reconcile answers 503 PAYOUT_PROVIDER_UNAVAILABLE and changes nothing. A rejected payout is returned unchanged.

No response contains a Stripe account id, an idempotency key or a credential.

What your players see

A player reads their own payouts with GET /player/v1/payouts: the award, status, currency, amount, Stripe transfer id and settlement time, without failure details.

When a payout settles, fails or is reversed, reward.payout-settled is published and can be delivered to your webhook endpoint. See the event catalog.

On this page