tenants
Operators, customers, people (memberships), invitations, teams and customer settings (currencies, addresses, alerts, approved domains, approval policies, primary owner, closure).
List customers (operator)
GET /v1/customers
- listCustomers
- API key scope customers:read
- Tenant operator
API key scope: customers:read (operator-level keys only, X-Tenant-Id = the operator id).
Metadata filter: filter[metadata.<key>]=<value> (exact match; at most 5; AND).
Batch look-up (P12-T07): filter[id]=<id>,<id>,… (at most 100) returns those rows only, e.g. to resolve names shown next to ids; combine with limit ≥ the number of ids.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
X-Tenant-Idrequired | header | Uuid | Tenant context: the operator tenant id (staff operations) or customer tenant id (customer operations). Validated against the caller's memberships; mismatch → |
X-Request-Id | header | string | Client-supplied correlation id. Generated by the server when absent; always echoed in the response and in problem details. |
cursor | query | string | Opaque cursor from |
limit | query | integer | Page size (max 100). |
sort | query | string | Sort order; prefix |
filter | query | object | Filters as |
q | query | string | Free-text search (trigram; min 2 characters). |
withTotal | query | boolean | Include |
Responses
400 Malformed request: unparseable JSON, or an invalid header, path or query parameter (`errors[].field` is `header.X`, `path.x` or `query.x`). Body validation failures are 422 `validation-error`.
application/problem+json401 Missing, expired or invalid credentials (`unauthenticated`, `session-expired` with `reason` `idle` / `revoked` / `expired`, `invalid-api-key`, `mfa-required`).
application/problem+json403 Authenticated but not allowed: missing capability or scope, tenant not in memberships, `step-up-required`, IP not allow-listed, impersonation restriction.
application/problem+json429 Rate limit exceeded.
application/problem+jsondefault Unexpected error (`500 internal-error`, `501 not-implemented`, `502 provider-error`, `503 service-unavailable`, `504 provider-timeout`). Money operations that time out at the provider are never retried blindly; their state becomes `exception`/unknown and is reconciled.
application/problem+json
Code samples
curl -X GET "https://bank.wirebloom.com/v1/customers" \
-H "Authorization: Bearer $WIREBLOOM_API_KEY" \
-H "X-Tenant-Id: $WIREBLOOM_CUSTOMER_ID"const response = await fetch("https://bank.wirebloom.com/v1/customers", {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.WIREBLOOM_API_KEY}`,
"X-Tenant-Id": process.env.WIREBLOOM_CUSTOMER_ID!,
},
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const result = await response.json();import os
import requests
response = requests.request(
"GET",
"https://bank.wirebloom.com/v1/customers",
headers={
"Authorization": f"Bearer {os.environ['WIREBLOOM_API_KEY']}",
"X-Tenant-Id": os.environ["WIREBLOOM_CUSTOMER_ID"],
},
timeout=30,
)
response.raise_for_status()Create a customer (operator-initiated)
POST /v1/customers
- createCustomer
- API key scope customers:write
- Tenant operator
Creates a draft customer. With primaryOwner a primary-owner invitation is emailed; without it (console sessions only) no invitation is sent. Creates a draft customer and invites the primary owner. Self-registration uses /auth/register.
API key scope: customers:write (operator-level keys only, X-Tenant-Id = the operator id).
API keys (review W3-02): an operator key needs customers:write, the owner address must be on the operator settings' apiKeyInvitationDomains (422 invitation_domains_required / domain_not_approved before anything is written) and each key may create 10 customers a minute (429).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
X-Tenant-Idrequired | header | Uuid | Tenant context: the operator tenant id (staff operations) or customer tenant id (customer operations). Validated against the caller's memberships; mismatch → |
X-Request-Id | header | string | Client-supplied correlation id. Generated by the server when absent; always echoed in the response and in problem details. |
Idempotency-Key | header | string | Optional idempotency key; same semantics as on money operations. |
Request body
Send customerType (P10-T03); identityType is derived (corporate for businesses, individual otherwise) and may be omitted; when both are sent they must agree (422). For sole traders and individuals legalName is the person's full name and incorporationCountry the country of residence; an individual has no registration number. primaryOwner is optional for console sessions: the customer is then created as a draft without an invitation, and staff invite the owner later (POST /ops/customers/{customerId}/owner-invitation). API keys must still send it (422 with field /primaryOwner).
legalNamestringrequiredtradingNamestring | nullidentityTypeIdentityTyperegistrationNumberstring | nullincorporationCountryCountryCoderequiredprimaryOwnerobjectFields of primaryOwner
emailEmailrequiredfirstNamestringrequiredlastNamestringrequired
pricingTemplateIdUuid | nullcustomerTypeCustomerTypemetadataMetadata
Responses
201 Created
application/jsonidUuidrequiredread-onlyFields of id
operatorIdUuidrequiredread-onlyFields of operatorId
numberstringrequiredread-only8-digit display id, unique per operator.
legalNamestringrequiredtradingNamestring | nullidentityTypeIdentityTyperequiredregistrationNumberstring | nullincorporationCountryCountryCoderequiredincorporationDateDate | nullstatusCustomerStatusrequiredread-onlyFields of status
riskRiskRating | nullapprovedAtTimestamp | nullclosedAtTimestamp | nullcreatedAtTimestamprequiredread-onlyFields of createdAt
updatedAtTimestampread-onlyFields of updatedAt
customerTypeCustomerTypemetadataMetadataaccessStatusAccessStatusread-onlyFields of accessStatus
400 Malformed request: unparseable JSON, or an invalid header, path or query parameter (`errors[].field` is `header.X`, `path.x` or `query.x`). Body validation failures are 422 `validation-error`.
application/problem+json401 Missing, expired or invalid credentials (`unauthenticated`, `session-expired` with `reason` `idle` / `revoked` / `expired`, `invalid-api-key`, `mfa-required`).
application/problem+json403 Authenticated but not allowed: missing capability or scope, tenant not in memberships, `step-up-required`, IP not allow-listed, impersonation restriction.
application/problem+json409 State conflict: `invalid-transition`, `duplicate-recipient`, `quote-expired`, `insufficient-funds`, `idempotency-key-in-use` (with Retry-After), `not-cancellable`.
application/problem+json422 Semantically invalid request; `errors[]` lists field-level problems. Also `idempotency-key-reused` when the key was used with a different payload, `limit-exceeded`, `recipient-not-approved`.
application/problem+json429 Rate limit exceeded.
application/problem+jsondefault Unexpected error (`500 internal-error`, `501 not-implemented`, `502 provider-error`, `503 service-unavailable`, `504 provider-timeout`). Money operations that time out at the provider are never retried blindly; their state becomes `exception`/unknown and is reconciled.
application/problem+json
Code samples
curl -X POST "https://bank.wirebloom.com/v1/customers" \
-H "Authorization: Bearer $WIREBLOOM_API_KEY" \
-H "X-Tenant-Id: $WIREBLOOM_CUSTOMER_ID" \
-H "Content-Type: application/json" \
--data '{
"legalName": "string",
"incorporationCountry": "GB"
}'const response = await fetch("https://bank.wirebloom.com/v1/customers", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.WIREBLOOM_API_KEY}`,
"X-Tenant-Id": process.env.WIREBLOOM_CUSTOMER_ID!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"legalName": "string",
"incorporationCountry": "GB"
}),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const result = await response.json();import os
import requests
response = requests.request(
"POST",
"https://bank.wirebloom.com/v1/customers",
headers={
"Authorization": f"Bearer {os.environ['WIREBLOOM_API_KEY']}",
"X-Tenant-Id": os.environ["WIREBLOOM_CUSTOMER_ID"],
"Content-Type": "application/json",
},
json={
"legalName": "string",
"incorporationCountry": "GB"
},
timeout=30,
)
response.raise_for_status()Get customer
GET /v1/customers/{customerId}
- getCustomer
- API key scope customers:read
- Tenant any
X-Tenant-Id is the customer itself or its operator. customer.view is held by operator staff roles and by every customer role (own customer only).
API key scope: customers:read (operator-level keys only, X-Tenant-Id = the operator id).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
customerIdrequired | path | Uuid | Identifier (customerId). |
X-Tenant-Idrequired | header | Uuid | Tenant context: the operator tenant id (staff operations) or customer tenant id (customer operations). Validated against the caller's memberships; mismatch → |
X-Request-Id | header | string | Client-supplied correlation id. Generated by the server when absent; always echoed in the response and in problem details. |
If-None-Match | header | string | Conditional GET; |
Responses
200 OK
application/jsonidUuidrequiredread-onlyFields of id
operatorIdUuidrequiredread-onlyFields of operatorId
numberstringrequiredread-only8-digit display id, unique per operator.
legalNamestringrequiredtradingNamestring | nullidentityTypeIdentityTyperequiredregistrationNumberstring | nullincorporationCountryCountryCoderequiredincorporationDateDate | nullstatusCustomerStatusrequiredread-onlyFields of status
riskRiskRating | nullapprovedAtTimestamp | nullclosedAtTimestamp | nullcreatedAtTimestamprequiredread-onlyFields of createdAt
updatedAtTimestampread-onlyFields of updatedAt
customerTypeCustomerTypemetadataMetadataaccessStatusAccessStatusread-onlyFields of accessStatus
304 Not modified (`If-None-Match` matched).
401 Missing, expired or invalid credentials (`unauthenticated`, `session-expired` with `reason` `idle` / `revoked` / `expired`, `invalid-api-key`, `mfa-required`).
application/problem+json403 Authenticated but not allowed: missing capability or scope, tenant not in memberships, `step-up-required`, IP not allow-listed, impersonation restriction.
application/problem+json404 Resource does not exist in this tenant (cross-tenant ids also return 404), or feature disabled (`feature-disabled`).
application/problem+json429 Rate limit exceeded.
application/problem+jsondefault Unexpected error (`500 internal-error`, `501 not-implemented`, `502 provider-error`, `503 service-unavailable`, `504 provider-timeout`). Money operations that time out at the provider are never retried blindly; their state becomes `exception`/unknown and is reconciled.
application/problem+json
Code samples
curl -X GET "https://bank.wirebloom.com/v1/customers/{customerId}" \
-H "Authorization: Bearer $WIREBLOOM_API_KEY" \
-H "X-Tenant-Id: $WIREBLOOM_CUSTOMER_ID"const response = await fetch("https://bank.wirebloom.com/v1/customers/{customerId}", {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.WIREBLOOM_API_KEY}`,
"X-Tenant-Id": process.env.WIREBLOOM_CUSTOMER_ID!,
},
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const result = await response.json();import os
import requests
response = requests.request(
"GET",
"https://bank.wirebloom.com/v1/customers/{customerId}",
headers={
"Authorization": f"Bearer {os.environ['WIREBLOOM_API_KEY']}",
"X-Tenant-Id": os.environ["WIREBLOOM_CUSTOMER_ID"],
},
timeout=30,
)
response.raise_for_status()Account currencies (offered and enabled)
GET /v1/customers/{customerId}/settings/currencies
- listCustomerCurrencies
- API key scope balances:read
- Tenant any
API key scope: balances:read.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
customerIdrequired | path | Uuid | Identifier (customerId). |
X-Tenant-Idrequired | header | Uuid | Tenant context: the operator tenant id (staff operations) or customer tenant id (customer operations). Validated against the caller's memberships; mismatch → |
X-Request-Id | header | string | Client-supplied correlation id. Generated by the server when absent; always echoed in the response and in problem details. |
If-None-Match | header | string | Conditional GET; |
Responses
200 OK
application/json304 Not modified (`If-None-Match` matched).
401 Missing, expired or invalid credentials (`unauthenticated`, `session-expired` with `reason` `idle` / `revoked` / `expired`, `invalid-api-key`, `mfa-required`).
application/problem+json403 Authenticated but not allowed: missing capability or scope, tenant not in memberships, `step-up-required`, IP not allow-listed, impersonation restriction.
application/problem+json404 Resource does not exist in this tenant (cross-tenant ids also return 404), or feature disabled (`feature-disabled`).
application/problem+json429 Rate limit exceeded.
application/problem+jsondefault Unexpected error (`500 internal-error`, `501 not-implemented`, `502 provider-error`, `503 service-unavailable`, `504 provider-timeout`). Money operations that time out at the provider are never retried blindly; their state becomes `exception`/unknown and is reconciled.
application/problem+json
Code samples
curl -X GET "https://bank.wirebloom.com/v1/customers/{customerId}/settings/currencies" \
-H "Authorization: Bearer $WIREBLOOM_API_KEY" \
-H "X-Tenant-Id: $WIREBLOOM_CUSTOMER_ID"const response = await fetch("https://bank.wirebloom.com/v1/customers/{customerId}/settings/currencies", {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.WIREBLOOM_API_KEY}`,
"X-Tenant-Id": process.env.WIREBLOOM_CUSTOMER_ID!,
},
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const result = await response.json();import os
import requests
response = requests.request(
"GET",
"https://bank.wirebloom.com/v1/customers/{customerId}/settings/currencies",
headers={
"Authorization": f"Bearer {os.environ['WIREBLOOM_API_KEY']}",
"X-Tenant-Id": os.environ["WIREBLOOM_CUSTOMER_ID"],
},
timeout=30,
)
response.raise_for_status()