External Bank Accounts

Create bank-level external bank accounts using raw routing details on the Cybrid platform.

Bank-level external bank accounts

Partners can connect external bank accounts by providing raw routing details.

  • The external bank account must be owned by KYC'd/KYB'd accounts on the Cybrid platform.
  • Supported for bank-level external bank accounts only (not customer-level).
  • Both sandbox and production environments require manual configuration.
ℹ️

Manual setup required

Contact partner support to complete the setup.

Account creation

You can create accounts for a Bank or a Customer.

To create accounts for your Bank, omit the customer_guid parameter in the request body. To create accounts for your Customers, include the customer_guid parameter in the request body.

ℹ️

ACH pull transfers

Only bank accounts connected via Plaid are supported for ACH pull transfers. Manual entry of bank account details is not supported for ACH pulls; Plaid connection is required.

⚠️

Ownership verification from 2026-09-01

From API version 2026-09-01, the Platform verifies ownership of a customer-owned account during account creation. Ownership of a counterparty account is implied to be the counterparty's, and the bank attests a bank-level account. Send the Accept-Version: 2026-09-01 header (or a later version) to onboard an account this way. On that version, account_kind: plaid_processor_token is not supported. For a customer-owned or bank-owned account_kind: raw_routing_details request, counterparty_name and counterparty_address are required as holder information. For a counterparty-owned account, you do not send them; see below.

For an account owned by a customer or a counterparty, the owner must already be verified: counterparty verification for a counterparty, KYC or KYB for a customer. At banks that verify individuals non-documentarily, an individual customer whose attested personal details have passed may create the account before verification completes, because the account is what authenticates them.

Counterparty-owned accounts

A request that includes counterparty_guid, for an individual or a business counterparty, creates the account with the counterparty's own verified details from when the counterparty was created. Do not send counterparty_name, counterparty_address or counterparty_email_address: sending any of them returns 400 with invalid_parameter.

Ownership of a counterparty account is implied to be the counterparty's, so there is no ownership check: no holder-name match, no bank_account identity verification and no Persona inquiry. The account usually moves storing → pending → completed. If the counterparty matches an existing identity that needs a compliance review, it moves to reviewing and ends completed, failed with compliance_rejection, or expired. The counterparty must already be verified, or the request returns 400 with unverified_counterparty. In sandbox, expected_behaviours is accepted and ignored for counterparty accounts.

Customer-owned and bank-owned raw routing accounts

For a customer-owned account (customer_guid without counterparty_guid) or a bank-owned account_kind: raw_routing_details account, counterparty_name and counterparty_address are the holder information, and both are required; for a customer-owned account, they are the input to the ownership check. The address must meet your bank's counterparty address requirements for its country; country_code is always required. Optional fields are validated when supplied. Omitting either field returns 400 with invalid_parameter; sending either as an empty object returns 400 with missing_holder_info.

If the customer's holder-name match is inconclusive, the account stays pending while the customer completes a Persona inquiry, where they can upload a bank statement. You do not create an identity verification for this: the Platform creates a type: bank_account, method: account_ownership one. Find it with GET /api/identity_verifications?external_bank_account_guid=<guid>, and once it is waiting with a persona_inquiry_id, open the inquiry for the customer with POST /api/persona_sessions. When the customer completes the inquiry, the account moves to reviewing, then ends completed, failed with ownership_mismatch, or expired if the verification lapses.

Bank-level accounts

Bank-level accounts, created without a customer_guid or a counterparty_guid, have no owner to verify. The precondition does not apply to them and no automated ownership check runs, because the bank attests the account itself. The account moves storing → pending → completed; if the provider rejects the bank's legal-entity details, it waits in reviewing while Cybrid repairs them. Accounts created on earlier versions keep their original behavior. See API Versioning.

State

StateDescription
storingThe Platform is storing the external bank account details in our private store
pendingThe Platform is verifying that the account owner owns the external bank account
reviewingThe ownership check was inconclusive, or the account is held for compliance review or repair, before it completes, fails, or expires
expiredThe ownership verification or compliance review lapsed before it was completed, so the account was never verified
completedThe Platform has created the external bank account
unverifiedThe external bank account is created, but it has not yet been verified
failedThe Platform was not able to successfully create the external bank account
refresh_requiredThe Platform has created the external bank account, but needs to be refreshed
deletingThe Platform is deleting the external bank account
deletedThe Platform has deleted the external bank account
ℹ️

API version required

The pending, reviewing, and expired states are returned from API version 2026-09-01. Send the Accept-Version: 2026-09-01 header (or a later version) to receive them. Earlier versions report pending and reviewing as storing, because an account in either state is still in flight, and report expired as failed, because it is terminal. See API Versioning.

Read account details

These notes apply to any external bank account you read with its bank account details, whichever owner it belongs to — bank, customer or counterparty. Note two things about the routing entries returned.

routing_number and routing_number_type are nullable. They are null for an identifier-only account, such as a counterparty account addressed by an Interac e-Transfer email or phone number, where there is no routing number to return.

An account can return more than one routing entry. A Plaid-linked account may return separate ACH and wire entries that share an account number but carry different routing numbers. Use payment_rail on each entry to tell them apart rather than assuming a single entry.

Failure codes

CodeDescription
invalid_routing_numberThe provided routing number is invalid
invalid_account_numberThe account number is invalid
invalid_account_typeThe account type is invalid
duplicateAn account with the same details already exists. This typically means the bank account has already been added previously, and attempting to add it again will result in a failed state with the duplicate failure code.
plaid_processor_tokenAn account could not be created due to an invalid Plaid processor token or an error with Plaid. Applies to accounts onboarded before API version 2026-09-01; from 2026-09-01, a request with account_kind: plaid_processor_token is rejected outright with 422 unsupported_value and no account is created.
plaid_multiple_accountsThe supplied Plaid token is associated with multiple accounts. Must only be a single account.
create_failedThe bank account and associated holder could not be created correctly. On 2026-09-01 and later, also returned when a permanent provider or validation error ends account creation
unverified_counterpartyThe counterparty account is unverified
ownership_mismatchThe account holder could not be shown to own the external bank account, either because the holder name did not match or because a reviewer denied the verification
compliance_rejectionCompliance reviewed the account holder and declined the external bank account; contact Cybrid Support
missing_holder_infoThe account holder's details could not be determined. On a plaid account the linked financial institution did not return an account holder name; re-link the account or create it with account_kind: raw_routing_details. Returned only for customer-owned or bank-owned accounts.
legal_entity_incomplete_piiThe financial institution could not create the account because the information held for the account holder is insufficient. For a counterparty-owned account, re-create the counterparty with the required information; for a customer-owned or bank-owned account, contact Cybrid Support — those records cannot be corrected through the API.
ℹ️

API version required

The ownership_mismatch, compliance_rejection, and missing_holder_info codes apply only to accounts created on API version 2026-09-01 or later. Send the Accept-Version: 2026-09-01 header (or a later version) on POST /api/external_bank_accounts to onboard an account this way. Ownership verification reports ownership_mismatch, a declined compliance review reports compliance_rejection, and the account creation that follows reports missing_holder_info. A verification or compliance review that lapses before it completes leaves the account in the expired state rather than reporting a failure code. A customer-owned or bank-owned raw_routing_details request that supplies counterparty_name or counterparty_address as an empty object is instead rejected synchronously with 400 and missing_holder_info, and no account is created; leaving either field out entirely returns 400 with invalid_parameter from parameter validation. From 2026-09-01, a permanent provider or validation error during account creation ends the account in failed with create_failed, or with legal_entity_incomplete_pii where the owner's legal-entity details are incomplete. A bank-owned account whose legal-entity details the provider rejects is the exception: it waits in reviewing while Cybrid repairs them. See API Versioning.


Did this page help you?