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.individualandtaxRequirements.organizationeach list thefieldsthe country takes for that type, in the order your form shows them. Each field has thekeyto send intaxIdentity, alabelfor your form, apatternthe value must match after uppercasing and removing whitespace, and anexamplewhere one exists.anyOflists the accepted combinations as sets ofkeys. Send every field of any one set. In France, for example, an individual sendstaxIdanddateOfBirth, orbirthPlaceCity,birthPlaceCountry, anddateOfBirth. An empty list means no tax data is required.- A field in
fieldsbut 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" },
…
]
}
]
}idis the value forpaymentMethod.type, andnameis a label for your form.currencieslists the codes a fiat rail takes aspaymentMethod.currency. A currency the rail does not settle in is 400VALIDATION_FAILEDonpaymentMethod.currency.assetslists the tokenscryptotakes aspaymentMethod.asset, each with itsnetworkand thecurrencyit is pegged to. A token without a peg, such as ETH, hascurrency: 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.