Skip to main content

API v3

v3 is where the BlockWyre API is going. It is served under /v3, next to /api/v2, and replaces it resource by resource. A v2 page that has a v3 replacement says so at the top and links to it. Both keep working during the transition (see Versioning).

Base URLhttps://api.blockwyre.com/v3 (sandbox: https://api-sandbox.blockwyre.xyz/v3). See Environments.
AuthenticationAuthorization: APIKey <signed-jwt>, the same header as /api/v2. See Authentication.
Content typeapplication/json
Rate limit100 requests per minute per client IP, shared by every key used from that IP. See Rate limits.

What is in v3​

SectionCoversReplaces in v2
BalanceBalances per account and bank accountBalance
CustomerCreating, listing and reading the workspace's accountsAccount: create, list, get
Individual verification (KYC)A person's verification record, its documents, and the hand-off to the verifierIndividual verification
Business verification (KYB)A business's verification record, its documents and its beneficial ownersBusiness verification
Bank accountAn account's bank accounts, their deposit instructions, movements and totalsDeposit Instructions, now per bank account; the rest is new
ACH pullFunding an account's USD bank account by debiting the holder's own account at another bank— (new)
ReportsVolume, fee and transaction reports over a date range— (new)
Crypto (beta)Crypto wallets, counterparties and withdrawals; beta, enabled per workspace. Until BlockWyre enables it, creating a wallet, saving, renaming or deleting a counterparty, and previewing or creating a withdrawal answer 404 feature_not_enabledCrypto: wallets, beneficiaries, withdrawals

What is different from v2​

IDs are TypeIDs​

Every id is a TypeID: a prefix naming the kind of thing, an underscore, and the UUID you know from v2 encoded in base32.

PrefixIs
cst_an account (v2's accountId)
bnk_a bank account
bpa_a pull account: the holder's account at another bank that an ACH pull debits
ach_an ACH pull
kyc_a person's verification record, including a business's beneficial owner
kyb_a business's verification record
doc_a verification document
wlt_a crypto wallet, or a crypto counterparty (an address you send to)
wtx_a crypto withdrawal
tnt_a workspace
apk_an API key

cst_01h455vb4pex5vsknk084sn02q and the UUID 01890a5d-ac96-774b-bcce-b302099a8057 are the same account. v3 paths and query parameters take the TypeID form only, and a raw UUID is refused with 400. Any TypeID library converts between the two.

Field names are snake_case​

account_id, bank_account_id, pending_credits, never accountId. This applies to query parameters too.

Money is an object​

{ "amount": "1250.75", "currency": "USD" }

amount is a decimal string, never a JSON number, so no digit is lost on the way.

Crypto withdrawals (beta) are the exception: their amount, fee and total are plain decimal strings next to the coin, not money objects. See Crypto v3.

Errors​

An error answers with this body, 4xx or 5xx:

{ "code": 404, "message": "bank_account_not_found" }
FieldIs
codeThe HTTP status, repeated.
messageA stable snake_case code to branch on (insufficient_balance, bank_account_not_found). Each endpoint lists its own.
detailsOnly when there is more to say, for example the fields a 422 validation failure names.

Some errors also carry errorCode, the HTTP status text ("Bad Request"); branch on message, not on it.

Three message values are not snake_case codes:

  • "failed to parse request" (400): the body is not valid JSON, or a value has the wrong type (for example, a malformed id).
  • "Validation failed" (422), with details: a field is missing or out of range.
  • "rate limit exceeded" (429): too many requests from your IP address; wait Retry-After seconds. See Rate limits.
{
"code": 422,
"errorCode": "Unprocessable Entity",
"message": "Validation failed",
"details": [{ "field": "body.email", "message": "email is a required field" }]
}

Two answers have no JSON body at all: a path that does not exist answers 404 with the plain text 404 page not found, and a method the path does not take answers 405 with an empty body.

401 unauthorized means the key, the signature or a claim failed, or the endpoint does not take this kind of key; 403 forbidden means the key authenticated but may not make this call. See 401 or 403 and Error Handling.

Lists​

Paginated lists take page (from 1) and limit (default 25, at most 100), and answer with this envelope. A limit over 100 is read as 100, not refused, and the response's page and limit are the ones actually used, so total_pages is computed from them. next_cursor and prev_cursor are reserved for cursor pagination and are not returned yet.

{
"data": [],
"page": 1,
"limit": 25,
"total_items": 0,
"total_pages": 0
}

An endpoint that returns a whole set at once (for example, balances) says so on its page. Only List Customers takes a sort parameter; see Pagination and Sorting.

Which account a request acts for​

  • An account-scoped key acts for its own account only. It does not need to name the account; naming a different one is refused (403), and another account's resources read as 404.
  • A workspace-scoped key acts for the whole workspace. Endpoints that act on one customer's money take an account_id query parameter, and refuse the request without it (400 account_id_required). Each endpoint says whether it takes one.
  • The workspace's own operations (listing, reading and creating customers, running their KYC and KYB) take a workspace-scoped key or a dashboard session. An account-scoped key is refused with 401.

Every key, workspace-scoped or account-scoped, holds the same fixed permission set, the API Key role, from the moment it is created: there is no role to assign. It covers everything this reference documents. Members, roles, API keys, workspace settings and account lifecycle actions (block, freeze, unfreeze) stay in the dashboard, and their endpoints refuse any key. See Key types and permissions.

Safe retries: Idempotency-Key​

The writes listed below take an Idempotency-Key header: one key per intended operation, sent again unchanged on every retry of that same operation. A key is any printable-ASCII string of 1 to 255 characters; a UUID is recommended. An empty Idempotency-Key counts as missing.

EndpointWithout the header
POST /v3/customersRefused: 400 idempotency_key_required.
POST /v3/banking/pull-accountsRefused: 400 idempotency_key_required.
POST /v3/banking/ach-pullsRefused: 400 idempotency_key_required.
POST /v3/bank-accountsGrace period for API keys until 2027-01-18: the request runs without the protection, and its response carries a Warning header. Dashboard sessions are refused already.
POST /v3/crypto/walletsSame grace period as POST /v3/bank-accounts.
POST /v3/crypto/withdrawalsSame grace period as POST /v3/bank-accounts.

The grace-period warning reads:

Warning: 299 - "Idempotency-Key will be required on this endpoint from 2027-01-18"
What happensAnswer
A retry with the same key and the same request, within 24 hoursThe first response again, with Idempotency-Replayed: true. The operation is not run twice.
The same key more than 24 hours after the first requestThe key has been forgotten: the request runs as a new one.
The same key on the same endpoint with a different request (other path values, query string or body)422 idempotency_key_conflict
The same key while the first request is still running409 idempotency_in_progress, with Retry-After
A key longer than 255 characters, or with a character outside printable ASCII400 idempotency_key_invalid
A keyed request whose body is over 1 MiB413 body_too_large: a request signed with an API key is capped at 1 MiB before its Idempotency-Key is read. (413 idempotency_body_too_large is only reachable from a dashboard session.)
The key store is unavailable503 idempotency_unavailable. Nothing ran: retry later with the same key.
  • What is replayed. 2xx and 4xx answers are stored and replayed byte for byte. A 5xx is not stored, so a retry after one runs the request again.
  • Headers. The response of a run or of a replay echoes Idempotency-Key and carries Idempotency-Replayed: false (it ran now) or true (a replay).
  • Scope. A key belongs to one caller (one API key, or one dashboard user) and one endpoint (method and route): two callers, or two endpoints, can use the same key without colliding.
  • Same request means the same path, query string and exact body bytes. Re-serializing the body differently on a retry makes it a different request.

Each endpoint's page says whether it takes the header.

Support and Resources​

If you need assistance or have any questions, our support team is here to help. You can contact our support team at support@blockwyre.com.

Stay Updated​

Stay up-to-date with the latest news, updates, and features from BlockWyre by following us on social media:

We are excited to have you on board and look forward to seeing how you leverage BlockWyre's powerful tools to enhance your financial operations. Happy integrating!