API Docs
Guides

Pay directly

Save the people and companies you pay as counterparties, then push payouts to their bank account, PayPal, or crypto wallet without a claim link.

You already hold the recipient's details, or you collect them in your own form. Save the recipient as a counterparty: a person or company in your team's book with name, address, payment method, and tax identity. A payout addressed to a counterparty is pushed to its saved payment method with POST /payouts/{id}/pay. No claim link, no recipient sign-in, no Talentir screen.

1. Read the country

The identity and address fields of a counterparty are fixed; the tax identity and the payment method are not. Which tax fields a counterparty needs depends on its country and type, and on your team's DAC7 setting. Do not hardcode them: read them from GET /countries when you build your form, and use the entry whose id is the address country.

curl "$BASE_URL/countries" \
  -H "Authorization: Bearer $TALENTIR_API_KEY"
{
  "object": "list",
  "data": [
    …,
    {
      "object": "country",
      "id": "DE",
      "name": "Germany",
      "taxRequirements": {
        "individual": {
          "fields": [
            { "key": "taxId", "label": "Tax identification number", "pattern": "^(\\d{10,13}|\\d{2,3}\\/\\d{3,4}\\/\\d{4,5})$", "example": null },
            { "key": "vatNumber", "label": "VAT number", "pattern": "^(DE)\\d{9}$", "example": "DE123456789" },
            { "key": "dateOfBirth", "label": "Date of birth", "pattern": null, "example": null }
          ],
          "anyOf": [["taxId", "dateOfBirth"]]
        },
        "organization": {
          "fields": [
            { "key": "taxId", "label": "Tax identification number", "pattern": "…", "example": null },
            { "key": "vatNumber", "label": "VAT number", "pattern": "^(DE)\\d{9}$", "example": "DE123456789" },
            { "key": "registrationNumber", "label": "Registration number", "pattern": "^[A-Z]{2,3}\\d{1,7}[A-Z]{0,2}$", "example": null }
          ],
          "anyOf": [["taxId", "registrationNumber"]]
        }
      }
    },
    …
  ]
}
  • taxRequirements.individual and taxRequirements.organization each list the fields the country takes for that type, in the order your form shows them. Each field has the key to send in taxIdentity, a label for your form, a pattern the value must match after uppercasing and removing whitespace, and an example where one exists.
  • anyOf lists the accepted combinations as sets of keys. Send every field of any one set. In France, for example, an individual sends taxId and dateOfBirth, or birthPlaceCity, birthPlaceCountry, and dateOfBirth. An empty list means no tax data is required.
  • A field in fields but in no set is optional, for example the VAT number.
  • The rules already reflect your team's DAC7 setting; you never evaluate DAC7 yourself.

GET /countries lists every country a counterparty can live in, ordered by id. A country Talentir does not pay to is not listed, and a counterparty with an address there fails with VALIDATION_FAILED.

2. Create the counterparty

curl -X POST "$BASE_URL/counterparties" \
  -H "Authorization: Bearer $TALENTIR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "customId": "creator-1042",
    "identity": { "type": "individual", "firstName": "Jane", "lastName": "Doe" },
    "email": "jane@example.com",
    "address": { "line1": "Musterstraße 1", "city": "Berlin", "postalCode": "10115", "country": "DE" },
    "paymentMethod": { "type": "bank_iban", "iban": "DE89370400440532013000", "currency": "EUR" },
    "taxIdentity": { "taxId": "12345678901", "dateOfBirth": "1990-05-04" }
  }'

identity.type is individual (with firstName and lastName) or organization (with name), and cannot change after creation. taxIdentity is checked against the country's rules for address.country and identity.type; a violation is 400 VALIDATION_FAILED with one issue per failing path.

The response is the counterparty. An empty missing means pay can succeed. Otherwise missing names the gaps: address, payment_method, or tax_identity. To list only the counterparties you can pay, use GET /counterparties?payable=true. A counterparty can be saved incomplete and completed later with PATCH /counterparties/{id}, where a nested object (address, paymentMethod, taxIdentity) is replaced as a whole and null clears it.

Choose the payment method

paymentMethod.type picks the rail: bank_iban (SEPA), bank_swift, bank_ach, bank_wire (Fedwire), bank_uk (Faster Payments), paypal, venmo, or crypto. Each type has its own account fields, documented in the reference. Which currencies a fiat rail settles in, and which tokens crypto pays out, can change without a new API version. Do not hardcode them: read them from GET /payment-method-types when you build your form.

curl "$BASE_URL/payment-method-types" \
  -H "Authorization: Bearer $TALENTIR_API_KEY"
{
  "object": "list",
  "data": [
    { "object": "payment_method_type", "id": "bank_iban", "name": "SEPA / IBAN", "currencies": ["EUR", "USD", "CHF", "GBP", "CAD", "CZK", "AUD"], "assets": null },
    …,
    {
      "object": "payment_method_type",
      "id": "crypto",
      "name": "Crypto",
      "currencies": null,
      "assets": [
        { "id": "eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "symbol": "USDC", "network": "Base", "currency": "USD" },
        …
      ]
    }
  ]
}
  • id is the value for paymentMethod.type, and name is a label for your form.
  • currencies lists the codes a fiat rail takes as paymentMethod.currency. A currency the rail does not settle in is 400 VALIDATION_FAILED on paymentMethod.currency.
  • assets lists the tokens crypto takes as paymentMethod.asset, each with its network and the currency it is pegged to. A token without a peg, such as ETH, has currency: null.

Pay to a crypto wallet

A crypto payment method takes the token to send as a CAIP-19 asset, and the walletAddress that receives it:

"paymentMethod": {
  "type": "crypto",
  "asset": "eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "walletAddress": "0x…"
}

Talentir checks that the address is valid on the asset's network, checksum included. An address that fails the check is 400 VALIDATION_FAILED on paymentMethod.walletAddress. An asset that is not in the crypto entry of GET /payment-method-types is 400 VALIDATION_FAILED on paymentMethod.asset. Talentir does not check who controls the wallet: you vouch for the address. A crypto transfer cannot be reversed, so confirm the address with the recipient before you pay.

A token without a peg (ETH, SOL, BTC) reads back with paymentMethod.currency: null. A payout to it sends the value of its amount in that token.

3. Create the payout and pay it

curl -X POST "$BASE_URL/payouts" \
  -H "Authorization: Bearer $TALENTIR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "customId": "INV-42",
    "description": "Invoice 42",
    "amount": "100.00",
    "currency": "EUR",
    "recipient": { "type": "counterparty", "counterpartyId": "counterparty_7c1d2f30-…" },
    "andThen": "approve_and_pay"
  }'

The payout fields are the same as for a payout link; only the recipient differs.

andThen: "approve_and_pay" creates, approves, and pays in one request. It needs payouts:approve and payouts:write, and either does all three steps or none: a counterparty whose missing is not empty fails the whole request with 422 COUNTERPARTY_NOT_PAYABLE and leaves no payout behind. The response is the payout in status requested.

Without payouts:approve, create the payout, have it approved, then call:

curl -X POST "$BASE_URL/payouts/INV-42/pay" \
  -H "Authorization: Bearer $TALENTIR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

pay needs payouts:write: it moves what approval already reserved. Paying a failed payout again also needs payouts:approve (see Handle the outcome). It is refused with 409 PAYOUT_NOT_PAYABLE when the payout is not approved or failed, with 422 RECIPIENT_NOT_COUNTERPARTY when its recipient is not a counterparty, and with 409 PAYOUT_NOT_YET_AVAILABLE before availableOn. pay does not schedule; call it on the day.

A counterparty with an email can receive a claim link instead of a push.

4. Handle the outcome

Settlement is asynchronous. Subscribe to webhooks for payout.completed and payout.failed, or poll GET /payouts/{id}.

A failed payout carries a failureCode to branch on (account_closed, account_invalid, name_mismatch, or other; an open enum) and a failureReason written for you, never shown to the recipient. The payout keeps its approval. Fix the counterparty with PATCH /counterparties/{id} and call pay again; the payout returns to requested. This second pay needs payouts:approve as well as payouts:write, because the money goes to the counterparty's current payment method, which the approval did not cover. Without payouts:approve, pay it again in the dashboard.

Changing a counterparty's address, payment method, or tax identity moves every approved payout to it back to created, because you now pay a different destination. Re-approve them afterwards.

In the sandbox, a counterparty whose identity.lastName or identity.name contains SANDBOX FAIL, whose PayPal email contains sandbox-fail, or whose wallet address is 0x000000000000000000000000000000000000dEaD, makes the payout fail. See the sandbox test scenarios.

Recipient-managed counterparties

When a recipient claims a payout, Talentir keeps their details as a counterparty with recipientManaged: true. Such a counterparty refuses PATCH and DELETE, because the recipient owns the data. To take over, create a counterparty with the same email; it replaces the recipient-managed one in lists and on new payouts.

Delete a counterparty

DELETE /counterparties/{id} is refused with 409 COUNTERPARTY_IN_USE while a payout to it is created, approved, requested, or failed.

After the delete, the counterparty is gone from the API. Every endpoint answers 404 COUNTERPARTY_NOT_FOUND for it, and lists do not include it. Payouts to it keep its id in recipient.counterpartyId. Its customId is free again for a new counterparty.