Payout Destinations
How to register where money goes.
POST /v1/customers/{customerId}/linked_accounts
The same object covers both kinds of destination. destinationType says which one you mean, either BANK_ACCOUNT or WALLET.
What you register
Send the country, the currency and the identifiers. You do not send a rail.
Alfred derives supportedRails[] from what you sent:
| You register | You get back |
|---|---|
| A CLABE in Mexico | [SPEI] |
| A US routing and account number | [ACH, WIRE] |
| A Hong Kong account | [FPS, CHATS] |
The fields a given route needs are listed in the route registry, on GET /v1/countries/{country}. Build your form from the registry, because that is what stays current.
{
"destinationType": "BANK_ACCOUNT",
"country": "MEX",
"currency": "MXN",
"holderName": "Proveedora del Norte S.A. de C.V.",
"bankAccount": {
"identifierType": "CLABE",
"identifier": "012180015555555555"
}
}A destination whose route isn't LIVE is refused at registration with route_unsupported. You find out at registration time instead of when you try to pay out.
Alfred verifies who owns the account
You don't declare whether a destination belongs to the customer or to a third party. Alfred decides it during verification, from the provider's account validation and a name match against the customer's verified identity, not from the holder name you send. The result is returned as relationship.relationship_type:
BANK_ACCOUNT_OWNERSHIPorWALLET_OWNERSHIPfor the customer's ownTHIRD_PARTY_BENEFICIARYfor anyone else
This matters because the third-party endorsement check keys off that value. You can't override it by asserting something different in your request.
Wallets
{
"destinationType": "WALLET",
"chain": "SOL",
"address": "..."
}A wallet destination needs only the chain and the address.
TRM screens every wallet, and that screening is the whole control. You don't sign a challenge, supply an attestation or complete a proof-of-control step on your side. The TRM result is what moves the relationship to VERIFIED, and it is also what refuses an address.
Wait for VERIFIED
Wait on linked_account.updated reaching relationship_status: VERIFIED.
A payout to a destination in any other state is refused with recipient_not_linked before anything is locked.
| Status | Can receive a payout |
|---|---|
PENDING | No, screening is still in progress. |
VERIFIED | Yes. |
REJECTED | No. |
EXPIRED | No. |
REVOKED | No. |
EXPIRED matters as much as REJECTED. A relationship has a validity window, so a destination that worked last quarter can stop working without anyone touching it. Integrations that only handle the move from PENDING to VERIFIED tend to miss this case.
The allowed_use_cases field is worth reading too. A destination approved for PAYOUT but not for COLLECTION will refuse a collection, and this field tells you before you try.
Reading and removing
GET /v1/customers/{customerId}/linked_accounts
GET /v1/linked_accounts/{linkedAccountId}
DELETE /v1/linked_accounts/{linkedAccountId}
The single read returns the derived supportedRails[], the relationship, the use cases it's approved for and its validity window.
Deleting revokes the relationship. Payouts already in flight still complete, and new ones are refused.
The same counterparty can be linked more than once. That is normal when someone has accounts on different rails or in different currencies.
In sandbox
POST /v1/test/linked_accounts/{linkedAccountId}/simulate_activation
This moves a relationship straight to VERIFIED without the review queue. The review queue is the one step of a payout whose timing you otherwise can't control, so simulating it lets you test a payout flow in a minute instead of waiting on someone else's process.
This path works on sandbox only. On production it returns 404.
Updated about 5 hours ago

