Create an External Account
Create an External Account (saved payment destination). Provisions synchronously against the configured payment provider; the account starts pending and flips to active or failed. Exactly one per-type detail object must match type. The owner discriminator determines whether this account belongs to a customer directly or to one of the customer’s counterparties.
Uniqueness (walletExternal). A wallet is registered at most once per owner: the key is (owner, normalized blockchain address, network family). Consequences:
- Re-registering an address the same owner already holds
active,pendingorinvalidreturns409 externalAccountAlreadyRegistered, withdetails.existingAccountIdnaming that account anddetails.existingAccountStatustelling you what to do next: -"pending"means a registration is still in flight and this is TRANSIENT — the account resolves toactiveorfailedon its own (typically within ~30 minutes; see the stale-pending sweep), after which re-registering afailedaccount re-drives it. Retrying the create immediately will not help; pollGETonexistingAccountId, or simply wait and re-submit later. -"active"(or"invalid") is TERMINAL: use the account named byexistingAccountIdinstead of retrying the create. - Two different owners under the same customer (the customer itself and one of its counterparties, or two counterparties) may each register the same address — each gets its own account,201. - The same address under two different customers is likewise allowed. - An account infailedorrejectedstate does not block re-registration: submitting it again re-drives provisioning on that same account (sameid) and adopts the newly submittedlabelandmetadata, returning201. Deleted accounts free their key entirely.
Uniqueness (bankUs with a custodial provider). When the project’s bank accounts are registered with a custodial provider, the provider holds a routing and account number once per customer, across the customer and all of its counterparties:
- Re-registering one the provider already holds for the customer, under any owner, returns
409 externalAccountAlreadyRegisteredwithdetails.existingAccountIdnaming that account anddetails.matchedOn=routingNumberAccountNumberCustomer. That is an account registered with the provider that isactive,invalid, orpendingonce the provider has accepted it. Use that account, or delete it before registering the number again. - When the provider itself reports the duplicate, for example when two registrations of the same number are sent at the same time, the409has nodetailsand the account the request tried to create is recorded asrejected. - Arejectedorfailedaccount does not block, nor does apendingone whose registration never reached the provider, or one registered with a different provider: the create registers a new account. - Deleting apendingaccount whose registration the provider never confirmed may not free the number at the provider. Registering it again can then return409withoutdetails; contact support to release it.
Send an Idempotency-Key and retry with the SAME key after a network failure: the original response is replayed, so a 409 always means a genuinely different request collided with an existing account.
Authorizations
Token from POST /auth/token
Headers
Required on POST and PUT requests. Use a unique value per logical mutation attempt, for example a UUID.
Body
Create an External Account. Exactly one per-type detail object must be supplied, matching type; the service validates the correct subset and rejects mismatches with 422.
Who owns this account - { kind: customer, customerId } or { kind: counterparty, counterpartyId }. Cards must be customer-owned.
- Customer-owned
- Counterparty-owned
The instrument type. Exactly one matching per-type detail object must be supplied.
bankUs, bankIban, bankCanada, walletExternal Optional display label.
Free-form key-value pairs stored on the resource and echoed back on reads.
Required when type = bankUs.
Required when type = bankIban.
Required when type = bankCanada.
Required when type = walletExternal.
Response
The request has succeeded and a new resource has been created as a result.
A saved payment destination registered for a customer or one of their counterparties. Write-only secrets (full account number, full IBAN) are never present on reads, only their last-4 renderings.
The customer the account is registered for, also when one of their counterparties owns it.
The account's type-specific values.
External Account ID (ext_ prefix).
^[a-z]+_([0-9a-hjkmnp-tv-z]{26}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$The account type; decides which details keys are present.
bankUs, bankIban, bankCanada, card, walletExternal The counterparty that owns the account. Left out when the account is the customer's own.
usd on bank accounts and cards. Left out on wallets, which list their assets in supportedDestinations.
Where this account can be paid, computed per account. A US bank account lists the rails it can receive on, narrowed by what your project and the platform enable; an account Erebor Bank, N.A. provisions reports ach, achSameDay and wire, so a missing rtp there means unverified, not refused. A wallet lists usdc and usdt on each network its family serves. Left out for IBAN and Canadian bank accounts and cards.
US bank accounts: rails the project has turned on that this account cannot be paid over, each with its reason.
Current lifecycle status. A transition to invalid always fires the externalAccount.statusChanged webhook.
active, pending, rejected, invalid, deleted, failed Why the account failed, was rejected or became invalid. Present only when status is failed, rejected or invalid.
Optional display label.
Free-form key-value pairs supplied at creation or update.
When the external account was registered.
When the external account was last updated.