Customers
Verification state — status only, never documents.
Jump to an operation:
List this business's customers — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
A business key lists only its own customers; an agent never lists across holders. L3-1: today's model allows at most ONE customer per account owner (docs/api/RESOURCE-MODEL.md D-3), so this is always a one-element or empty list, never paginated in practice — has_more is always false. Empty (never 404) before customers.create has ever been called for this account. Fix-pack (independent review, P2): a customer record that exists but whose mapping to our verification partner is stale answers the SAME 409 customer_mapping_stale GET /v1/customers/{id} answers for it — never a retry-forever 503. Dark-flag — gated behind api_v1.customers (K6), the same flag customers.get/.create already use — no new flag.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List this business's customers — dark-flag › Responses
A page of customers.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Start verification for a new customer — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.write· Test mode: unavailable · Money boundary: per-transaction human confirmation requiredThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Feeds the account-type choice (onb-welcome, R17 §1) into a new not_started customer. Never a public token — a customer is created only by the business key that owns it or by the agent acting for one holder, and it is ALWAYS that caller's own account holder (RESOURCE-MODEL D-3) — never a customer-of-a-customer. The request body never carries a name, email, date of birth, address, document or tax id, with ONE exception below; the server resolves the caller's account owner internally and reads its own email, and — for customer_type: individual — its own name, when a real first+last pair is already on file (never a merchant/brand name, or the email local part). Round-2 architect ruling (2026-09-21): when no trustworthy name is on file, the SAME hosted verification flow starts WITHOUT one — our verification partner's hosted KYC reads the legal name off the identity document itself, never off this request. This removes the round-1 409 customer_name_required refusal, which had no /v1 remedy and permanently blocked every API-only account (fix-pack, independent review, P1 — closed, not narrowed). For customer_type: business, the body may instead carry an OPTIONAL legal_name (1-200 chars, trimmed) — a registered company name, not personal data — which becomes that entity's own legal name on file; omitted, the business customer is created without one. legal_name on an individual request answers 400 invalid_request (param: legal_name) — it is a business-only field. If the account already has a customer, 409 customer_already_exists — our verification partner bills per terminal KYC status, so this never creates a second one; a concurrent or retried call that already claimed the account's onboarding slot for a DIFFERENT customer_type answers 409 customer_type_conflict instead (A4-FIX-L3). Owner only — a bearer session must be the account's owner, never an admin (architect ruling, 2026-09-21): this always acts as the account's oldest owner regardless of which member calls it (an admin-minted business key still reaches this route — key minting is the gate there, POST /v1/api_keys, owner or admin). country, when given, is validated against the same market registry POST /v1/accounts validates it against (400 invalid_request for an unrecognised code) and never overwrites a country the account already declared. A test-mode key (livemode: false) answers 503 temporarily_unavailable here — our verification partner's own sandbox toggle is process-wide, not per-caller, so a test-mode call would otherwise reach the SAME live, billed account (fix-pack, independent review, P2; matches orders.create/quotes.create). The 201 response carries NO hosted link — mint one with the existing POST /v1/customers/{id}/verification_links (kind: tos first, per R17-verification.md). Dark-flag — gated behind api_v1.customers (K6), the same flag every other customers.* operation already uses — no new flag.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Start verification for a new customer — dark-flag › Request Body
customer_typeWhich kind of verification to start: a natural person, or a business. Never written to the account: Account.customer_type follows the type Swaps records for the customer, which is this one for a new customer and the customer's own type when our verification partner already holds a customer for the account's owner (the 201 body's customer_type shows it).
country^[A-Z]{2}$ISO 3166-1 alpha-2, when known — validated against the SAME market registry POST /v1/accounts validates it against (400 invalid_request, param: country, for a well-formed but unrecognised code — fix-pack, independent review, P2), then persisted ONLY when the account has no country on file yet; a country the account already declared is never silently overwritten. Does not yet select a payment rail for this customer — a future PR can map country to a rail once product confirms which one.
legal_namecustomer_type: business ONLY — the registered company name, trimmed, forwarded to our verification partner as the entity's own legal name. Not personal data (round-2 architect ruling, 2026-09-21): a business's registered name is fine to accept from the caller, unlike a natural person's legal name, which this endpoint never accepts from any request body. 400 invalid_request (param: legal_name) when customer_type is individual. Omitted, a business customer is created without a name on file — same treatment an individual with no name on file gets.
Start verification for a new customer — dark-flag › Responses
The new customer, verification_state: not_started.
id^cus_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectcustomer_typeverification_stateSwaps' own verification-state vocabulary (R17 §3): not_started (verification not yet begun), pending (submitted, awaiting review), under_review (a review is actively in progress), approved (cleared), rejected (declined — see rejection_outcome/rejection_guidance), stale_mapping (our record of this customer needs an operator to repair it before verification can continue), action_required (a requirements_due[] entry is blocking progress). Normalized to these seven values today, but the field is marked forward-compatible (x-swaps-open-enum) — treat an unrecognized member as opaque rather than a deserialization failure.
requirements_outstandingCount backing requirements_due[].
endorsementsThe payment rails this customer is cleared for (R17 §3) — Swaps' own rail codes: base (the crypto/stablecoin base rail every customer needs first), sepa (EUR via SEPA), spei (MXN via SPEI), pix (BRL via Pix), faster_payments (GBP via Faster Payments), cop (COP via bank transfer) — the array is open: a rail code outside this list is possible and must be ignored, never treated as an error.
Per-rail status — one entry per rail for which the upstream snapshot carries a per-rail status object, including rails not yet approved; it can be empty even when endorsements[] is not. Each item names its own rail in endorsement; never pair by index with endorsements[], which lists only the approved subset and can be shorter or differently ordered.
next_stepThe single next step that unblocks verification (source text for get_verification_status).
tos_statusTerms-of-service acceptance status, as our verification partner reports it today — not yet normalized onto a closed Swaps enum (unlike verification_state above); treat any non-approved value as "not yet accepted" rather than enumerating every possible string.
kyc_statusKYC/identity-verification status, as our verification partner reports it today — already folded into verification_state above for the canonical read; not yet normalized onto its own closed Swaps enum.
available_actionsstaleRestart onboarding — survives to the wire, never collapsed into another state.
degradedCached — we could not reach the provider. Survives to the wire, never collapsed.
rejection_outcomeAn outcome enum converted server-side from developer_reason — never the raw provider reason (RESOURCE-MODEL §2.4). Known values include verification_unavailable and terminal; the full set is not yet published upstream.
rejection_guidanceBounded, safe-copy only — never developer_reason passthrough (V-G3).
compliance_review_stateassociated_persons_countA count, never a name — the only UBO fact on the base object (V-G4).
Read this customer's verification status — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Self only — a customer reads its own record, and an agent reads the one holder it acts for. Status only: never a document, a submitted KYC form value, a bank detail or a beneficial owner's name. Dark-flag — gated behind api_v1.customers (K6). An email log/persist leak on the live status-read's unauthorized branch (D-11, SEC-5) is fixed in the same PR that wires this read (swaps/index.ts's probe fingerprint no longer carries email). BL-40: 404 not_found means no customer has ever been created for this account. A customer that exists but whose live verification-status read failed (a stale internal mapping, or a transient read failure) answers 503 temporarily_unavailable instead — retry, never treat it as day-zero.
{id} is the account's own cus_… id, from customers.list or customers.create. The literal me is a deprecated alias for it (see the parameter below). Before POST /v1/customers has ever been called for this account, every id answers this exact 404 not_found; call customers.create first.
path Parameters
id^(cus_.+|me)$ · requiredThe customer id (cus_…). The literal me (the caller's own account holder) is a deprecated alias, accepted until the next Swaps-Version date: read the id from customers.list or customers.create.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Read this customer's verification status — dark-flag › Responses
The customer's current verification status.
id^cus_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectcustomer_typeverification_stateSwaps' own verification-state vocabulary (R17 §3): not_started (verification not yet begun), pending (submitted, awaiting review), under_review (a review is actively in progress), approved (cleared), rejected (declined — see rejection_outcome/rejection_guidance), stale_mapping (our record of this customer needs an operator to repair it before verification can continue), action_required (a requirements_due[] entry is blocking progress). Normalized to these seven values today, but the field is marked forward-compatible (x-swaps-open-enum) — treat an unrecognized member as opaque rather than a deserialization failure.
requirements_outstandingCount backing requirements_due[].
endorsementsThe payment rails this customer is cleared for (R17 §3) — Swaps' own rail codes: base (the crypto/stablecoin base rail every customer needs first), sepa (EUR via SEPA), spei (MXN via SPEI), pix (BRL via Pix), faster_payments (GBP via Faster Payments), cop (COP via bank transfer) — the array is open: a rail code outside this list is possible and must be ignored, never treated as an error.
Per-rail status — one entry per rail for which the upstream snapshot carries a per-rail status object, including rails not yet approved; it can be empty even when endorsements[] is not. Each item names its own rail in endorsement; never pair by index with endorsements[], which lists only the approved subset and can be shorter or differently ordered.
next_stepThe single next step that unblocks verification (source text for get_verification_status).
tos_statusTerms-of-service acceptance status, as our verification partner reports it today — not yet normalized onto a closed Swaps enum (unlike verification_state above); treat any non-approved value as "not yet accepted" rather than enumerating every possible string.
kyc_statusKYC/identity-verification status, as our verification partner reports it today — already folded into verification_state above for the canonical read; not yet normalized onto its own closed Swaps enum.
available_actionsstaleRestart onboarding — survives to the wire, never collapsed into another state.
degradedCached — we could not reach the provider. Survives to the wire, never collapsed.
rejection_outcomeAn outcome enum converted server-side from developer_reason — never the raw provider reason (RESOURCE-MODEL §2.4). Known values include verification_unavailable and terminal; the full set is not yet published upstream.
rejection_guidanceBounded, safe-copy only — never developer_reason passthrough (V-G3).
compliance_review_stateassociated_persons_countA count, never a name — the only UBO fact on the base object (V-G4).
Mint a hosted verification link — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Accepts all five kind values, but only tos and kyc mint a real link today, through our verification partner's own hosted flow (K6). business_questionnaire, business_ubo and remediation have no backing implementation anywhere in this codebase yet — the partner's endorsement-scoped link shape must be confirmed to distinguish them before they ship for real, so each answers 409 capability_unavailable rather than a fabricated link. return_to is accepted and validated (same-origin) but not yet threaded into the underlying hand-off — wiring a real override is tracked as a follow-up. Owner only — a bearer session must be the account's owner, never an admin (architect ruling, 2026-09-21): this always acts as the account's oldest owner regardless of which member calls it (an admin-minted business key still reaches this route — key minting is the gate there, POST /v1/api_keys, owner or admin). Dark-flag — gated behind api_v1.customers (K6).
path Parameters
id^(cus_.+|me)$ · requiredThe customer id (cus_…). The literal me (the caller's own account holder) is a deprecated alias, accepted until the next Swaps-Version date: read the id from customers.list or customers.create.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Mint a hosted verification link — dark-flag › Request Body
kindOne polymorphic mint across every hosted verification hand-off (D-52, V-G17): tos (terms of service), kyc (identity/business verification), business_questionnaire (compliance questionnaire), business_ubo (beneficial-owner check), remediation (a requested fix to a prior submission).
return_toRequired, and validated same-origin (https://swaps.app/https://www.swaps.app) — but NOT YET threaded into the underlying hosted hand-off, so the dead end where every live hand-off lands on /confirm instead of the hub (D-52, V-G18, prod P-B3) is not yet closed. This field is accepted and checked, not silently ignored, but is not yet "honoured" in the sense of actually steering the redirect; that wiring is tracked as a follow-up.
Mint a hosted verification link — dark-flag › Responses
A one-time hosted link.
urlexpires_atList this business customer's beneficial owners, name-free — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
{label} plus an OPTIONAL ownership_percent, never a name — for every caller kind, in this K6 pass (stricter than D-49's own default, which would allow name for the holder's own delegated session; that widening is not implemented here). Our verification partner's raw per-person field names are not independently confirmed anywhere in this codebase (only a count is read today) — ownership is read defensively, never guessed at, and is OMITTED (never defaulted to 0) when the raw payload carries no recognized ownership field, so a client must render the absence honestly rather than as an observed 0%. status is omitted (optional on the schema) rather than a fabricated placeholder — no confirmed per-person status field exists anywhere in this codebase yet; a follow-up derives it from the same endorsement missing[] object-scoped bundles the Verification Hub already reads, once a live upstream response confirms the shape. Dark-flag — gated behind api_v1.customers (K6).
path Parameters
id^(cus_.+|me)$ · requiredThe customer id (cus_…). The literal me (the caller's own account holder) is a deprecated alias, accepted until the next Swaps-Version date: read the id from customers.list or customers.create.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List this business customer's beneficial owners, name-free — dark-flag › Responses
This customer's associated persons — name-free unless the caller is the holder's own delegated session.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Submit structured compliance details (V4 — Questionnaires) — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: business key, agent (business key), dashboard session · Scope:
customers.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Write-only structured update for our verification partner's own compliance-questionnaire fields — this route never re-implements the enum whitelist or the underlying call, it only resolves the caller's own customer id server-side (never accepted from the request body — R17 PII rule) and forwards the body verbatim. Every field is optional; only the fields present are sent — the server definedOnly-merges, exactly as the underlying action already does. Closes V-G5 (R17-verification.md), the same kind of gap K6 already closed for tos/kyc via verification_links. Responds 202 with {accepted: true} only — per V-G5/V-G15, a submitted value is NEVER echoed back on any read (there is no GET counterpart by design; GET /customers/{id} still carries only status). Test mode: full — this route does not read caller.livemode; a sk_test_ caller's answers are written to the SAME live account a sk_live_ caller would reach (the underlying action has no test/live branch today, matching its sibling customers.verification_links.create). Dark-flag — gated behind api_v1.customers (K6).
path Parameters
id^(cus_.+|me)$ · requiredThe customer id (cus_…). The literal me (the caller's own account holder) is a deprecated alias, accepted until the next Swaps-Version date: read the id from customers.list or customers.create.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Submit structured compliance details (V4 — Questionnaires) — dark-flag › Request Body
account_purposeThe union of the individual (11) and business (13) account_purpose values (16 unique — some overlap). Which of these are valid is further restricted by the customer's own customer_type; a mismatched-but-listed value still gets 400.
account_purpose_otherFree text — only meaningful (and only read) when account_purpose is other. 400 past 1024 characters (never silently truncated) — see this schema's own description.
source_of_fundsThe union of the individual (12) and business (11) source_of_funds enum values (22 unique — one overlap, pension_retirement).
source_of_funds_descriptionFree text. 400 past 1024 characters (never silently truncated) — see this schema's own description.
employment_statusIndividual only — current employment status, Swaps' own six-value vocabulary.
expected_monthly_payments_usdExpected monthly payment volume through Swaps, banded in USD.
business_descriptionFree text — the business's own description of what it does. 400 past 1024 characters (never silently truncated) — see this schema's own description.
business_typeBusiness only — legal structure, Swaps' own seven-value vocabulary.
business_industry^[0-9]{6}$A single 2022 NAICS six-digit code identifying the business's industry — a public US federal classification standard, not a Swaps- or partner-specific list. Too large to enumerate here, so only the ^[0-9]{6}$ shape is checked at this layer; the closed set of valid codes is enforced server-side, never free text.
estimated_annual_revenue_usdBusiness only — estimated annual revenue, banded in USD.
high_risk_activitiesZero or more of 15 regulated-activity values (includes none_of_the_above).
Submit structured compliance details (V4 — Questionnaires) — dark-flag › Responses
Intake confirmed — the fields were forwarded to our verification partner. Not a re-read of the stored profile.
acceptedList one customer's verification timeline — planned
Status: Planned — not built yet · Callers: business key, agent (business key), dashboard session · Scope:
customers.read· Test mode: sandboxThis operation is planned — not built yet. It is documented for the contract it will carry, but it does not run today. Never call it expecting a live result.
customer.verification_state_changed, .rejected, .requirements_updated (RESOURCE-MODEL §3, D-55) — gives a dated ladder for the review-in-progress screens without re-deriving readiness client-side. Proposed.
path Parameters
id^(cus_.+|me)$ · requiredThe customer id (cus_…). The literal me (the caller's own account holder) is a deprecated alias, accepted until the next Swaps-Version date: read the id from customers.list or customers.create.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List one customer's verification timeline — planned › Responses
This customer's event timeline, oldest first.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.