API Docs
Concepts

Idempotency

Retry any POST safely with an Idempotency-Key, and name payouts with a customId.

Two mechanisms keep a retried request from doing its work twice. They do different jobs.

Idempotency-Key

Send an Idempotency-Key header on every POST. It is a client-chosen string of 1 to 255 characters; a UUID is recommended.

  • The same key on the same endpoint for the same team within 24 hours replays the stored response, with the header Idempotent-Replayed: true. The retry does no work.
  • The same key with a different body is 409 IDEMPOTENCY_KEY_REUSED.
  • The same key while the first request is still running is 409 IDEMPOTENCY_KEY_IN_PROGRESS. Retry after a moment.
  • A 500 INTERNAL_ERROR is stored like any other response, because the request can have done part of its work before the fault. Its replay does not run the request again. Check the state first, for a payout with GET /payouts/{customId}, then send the request with a new key if it is still needed.

So a timeout-and-retry loop is safe: send the identical request again and you get the original response back.

curl -X POST "$BASE_URL/payouts" \
  -H "Authorization: Bearer $TALENTIR_API_KEY" \
  -H "Idempotency-Key: payout-create:INV-42" \
  -H "Content-Type: application/json" \
  -d '{ "customId": "INV-42", "description": "Invoice 42", "amount": "100.00", "currency": "EUR", "recipient": { "type": "email", "email": "jane@example.com" } }'

customId

A customId is your own key for a payout or a counterparty: an order id, a campaign line, an invoice number. It is unique within your team and per resource.

A payout's customId is used once: it stays taken after the payout is deleted, so a repeated create can never pay twice. A deleted counterparty is gone from the API, and its customId is free again for a new counterparty.

  • POST /payouts only creates. A second create with the same customId is 409 CUSTOM_ID_TAKEN, and details.id names the payout that holds it. details.id is null when a deleted payout holds it.
  • A customId works in place of the id wherever a payout or a counterparty is referenced: the {id} in a path (GET /payouts/campaign-42), the ids of a batch request, recipient.counterpartyId, the payoutId and payoutIds of a session, and the recipientCounterpartyId filter. Responses always carry the prefixed id.
  • A new or changed customId has 1 to 255 characters: ASCII letters, digits, ., _, :, and -, starting with a letter or a digit. Such a key is safe in a URL path as is. A key saved earlier through v1 that does not follow this format keeps working in every lookup and is returned as it is.
  • A customId must not start with the resource prefix (payout_, counterparty_), so the two key spaces stay apart.

Use both

The key says which request attempt this is, for 24 hours. The customId says which payout this is, forever. For a create, derive the key from your business key, for example Idempotency-Key: payout-create:INV-42. A retry inside 24 hours replays the 201. A retry later gets 409 CUSTOM_ID_TAKEN with the payout's id. Both end on the same payout.

Locking a payout down

If the payout must not change after creation — for example when the request data comes from a signed source — create it with "locked": true. A locked payout refuses PATCH and dashboard edits with 409 PAYOUT_LOCKED. Only delete is left.

What is not idempotent

Session minting (POST /sessions) returns a fresh URL each call by design; the header still replays a retry. Webhook creation replays with the header too; without it, check GET /webhooks before subscribing if you might retry.