# Developers — Swaps API

API keys, webhook endpoints, events, usage.

**Jump to an operation:**

- [GET /account](#read-the-signed-in-holders-own-account-available)
- [PATCH /account](#update-the-holders-own-profile-fields-available)
- [GET /accounts](#list-every-account-this-login-can-reach-available)
- [POST /accounts](#create-another-account-under-the-same-login-not-open-today-available)
- [GET /account/readiness](#resolve-the-one-next-action-readiness-kind-available)
- [GET /account/setup_guide](#read-the-six-section-onboarding-checklist-available)
- [POST /account/sessions/revoke_all](#sign-the-holder-out-of-every-device-planned)
- [GET /activity](#list-the-cross-product-activity-ledger-available)
- [GET /activity/summary](#read-unbounded-counts-across-every-product-available)
- [GET /events](#list-the-cross-domain-event-stream-available)
- [GET /events/{id}](#read-one-event-by-id-available)
- [GET /events/stream](#subscribe-to-events-over-server-sent-events-available)
- [GET /webhook_endpoints](#list-the-accounts-webhook-endpoints-available)
- [POST /webhook_endpoints](#register-a-new-webhook-endpoint-available)
- [GET /webhook_endpoints/{id}](#read-one-webhook-endpoint-available)
- [PATCH /webhook_endpoints/{id}](#update-url-event-types-description-or-enabled-state-available)
- [DELETE /webhook_endpoints/{id}](#remove-a-webhook-endpoint-available)
- [POST /webhook_endpoints/{id}/rotate_secret](#rotate-the-endpoints-signing-secret-available)
- [POST /webhook_endpoints/{id}/send_test_event](#send-a-synthetic-test-event-to-this-endpoint-available)
- [GET /webhook_deliveries](#list-webhook-delivery-attempts-available)
- [GET /webhook_deliveries/{id}](#read-one-webhook-delivery-available)
- [POST /webhook_deliveries/{id}/replay](#replay-a-webhook-delivery-available)
- [POST /webhook_waitlist](#join-the-webhook-delivery-waitlist-available)
- [POST /card_waitlist](#join-the-card-waitlist-available)
- [GET /address_book](#list-saved-payment-destinations-available)
- [POST /address_book](#save-a-new-payment-destination-available)
- [GET /address_book/{id}](#read-one-saved-destination-available)
- [PATCH /address_book/{id}](#rename-a-saved-destination-available)
- [DELETE /address_book/{id}](#remove-a-saved-destination-available)
- [POST /address_book/{id}/recheck](#re-screen-a-saved-crypto-address-available)
- [POST /address_book/{id}/beneficiary](#update-the-travel-rule-beneficiary-counterparty-details-for-a-saved-destination-available)
- [GET /credits](#read-the-holders-credit-balance-available)
- [GET /credit_events](#list-the-credit-ledger-available)
- [POST /credit_checkouts](#start-a-stripe-checkout-for-a-credit-pack-available)
- [GET /api_keys](#list-the-accounts-api-keys-available)
- [POST /api_keys](#create-a-new-api-key-available)
- [GET /api_keys/{id}](#read-one-api-key-available)
- [POST /api_keys/{id}/roll](#rotate-a-keys-secret-available)
- [POST /api_keys/{id}/revoke](#revoke-a-key-permanently-available)
- [GET /api_keys/{id}/usage](#read-one-keys-today-only-usage-available)
- [GET /request_logs](#list-raw-per-call-telemetry-for-own-keys-available)

> Generated from the bundled OpenAPI contract. The HTML page and this Markdown twin share the same processed operation descriptions and status annotations.

<a id="read-the-signed-in-holders-own-account-available"></a>
## `GET /account` — Read the signed-in holder's own account — available

**Operation ID:** `account.get`

> **Status:** Available · **Callers:** dashboard session, agent (business key), business key · **Scope:** `account.read` · **Test mode:** full

The caller's own profile — display name, email, verification tier, locale, access state, the persona's `market` (BL-25, D-PF-9a — country + default currency for the embedded Buy & sell widget) and the dashboard's saved preferences. Reads "the selected account" (D-109: `Swaps-Account`, or the caller's default) — never a lookup by id; `404` names the pre-existing, rare case where the account row a valid credential resolves to is itself gone. `?expand=readiness,setup_guide` (API-PERF-1 M5) merges either or both of those reads' payloads in under `readiness`/`setup_guide` — folding the dashboard's own account-load sequence into one round trip instead of three; omitted when not requested. A REQUESTED expansion that fails (fix round 1) never fails the account read or answers a `404` of its own: the account, and any other requested expansion that succeeded, are still published at `200`, and `expand_errors` names which field is missing and why. `caller` names who is calling and, for a session, its role on this account, so a client can withhold an owner/admin-only action (e.g. `PATCH /account`) from a `member` instead of learning it from `403 role_denied`. Callers: dashboard session, agent (holder), business key (read-only). Test mode: full. With an `sk_test_` business key on a test twin, `email` is the only field read from the live owner; the other user-level fields are defaults, never the owner's values: `language` and `support_contact` are `null`, `notification_preferences` is `{marketing: false, product_tips: true}` and `dashboard_preferences` is `{}`. A test-mode dashboard session sees its own values. `caller` describes the credential, not the owner, and is published in both modes (`business_key`, role `null`, for a key). `verification` (`live` by default) applies to the expansions: with `snapshot`, `readiness` and `setup_guide` are built from Swaps' stored Bridge check while it is no more than 24 hours old, each names when its facts were checked (`verification_checked_at`), and a live read that cannot reach Bridge fails both as `temporarily_unavailable` in `expand_errors`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `AccountExpand` | component | — | — | Reusable parameter component |
| `VerificationRead` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The account. When `?expand=` was requested, may additionally carry `readiness`/`setup_guide` (on success) and/or `expand_errors` (on a per-field failure — see the operation description). | `application/json` [Account](/reference/~schemas.md#account) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="update-the-holders-own-profile-fields-available"></a>
## `PATCH /account` — Update the holder's own profile fields — available

**Operation ID:** `account.update`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `account.write` · **Test mode:** unavailable

Updates display name, language, notification preferences, dashboard preferences and the opt-in support contact (L4-9) payer surfaces may show. Email, country, customer type and access state are not writable here — email is an identity-plane change and the rest are derived elsewhere. Callers: dashboard session, agent (holder), never a business key. Test mode: unavailable (503). Language, dashboard preferences, notification preferences and the support contact belong to the holder and are shared with live mode; the handler also refuses them from a test-mode caller with `400 invalid_request`, code `livemode_boundary`, before anything is written.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [AccountUpdateRequest](/reference/~schemas.md#accountupdaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The updated account. | `application/json` [Account](/reference/~schemas.md#account) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-every-account-this-login-can-reach-available"></a>
## `GET /accounts` — List every account this login can reach — available

**Operation ID:** `accounts.list`

> **Status:** Available · **Callers:** dashboard session, agent (business key), business key · **Scope:** `account.read` · **Test mode:** full

"Mine" — every account the caller holds a membership on (D-109). One live account per login is supported today; a login that already held more than one keeps seeing all of them here and can still select any of them with `Swaps-Account`. A business key is bound to a single account and always sees a one-item list. Callers: dashboard session, agent (holder), business key (read-only). Test mode: full. With an `sk_test_` business key on a test twin, `email` is the only field read from the live owner; the other user-level fields are defaults, never the owner's values: `language` and `support_contact` are `null`, `notification_preferences` is `{marketing: false, product_tips: true}` and `dashboard_preferences` is `{}`. A test-mode dashboard session sees its own values.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of accounts. | `application/json` [AccountList](/reference/~schemas.md#accountlist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="create-another-account-under-the-same-login-not-open-today-available"></a>
## `POST /accounts` — Create another account under the same login — not open today — available

**Operation ID:** `accounts.create`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `account.write` · **Test mode:** unavailable

Not open today: one account per login is supported (since 2026-10-05). A login that already owns a live account gets `409 conflict`, code `account_already_exists`, and nothing is written: no account and no membership. Every signed-in login is given its account on its first request without `Swaps-Account`, and only the owner of the live account a request acts on gets past the earlier refusals, so through `/v1` this operation never answers `201` today: a request that gets past them gets this `409`. Every refusal this operation answered before still comes first, unchanged: `403` for a business key or a `member`, `503` for a test-mode session, `400` for a malformed body or an unrecognised `country`. An account's type follows its owner's Bridge customer (see `Account.customer_type`). Opening a second account under one login waits for a later program that gives every account its own verification; the request and the `201` below are the contract it will carry, an individual or a business account whose owner is the caller. A business key cannot call this — it is bound to one account and never mints another. Callers: dashboard session only. Test mode: unavailable (503) — a new account is always live.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [AccountCreateRequest](/reference/~schemas.md#accountcreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The new account — the contract a later program will open. Today no `/v1` caller receives it: a request that gets past the earlier refusals always comes from the owner of the live account it acts on, and gets the `409` below. | `application/json` [Account](/reference/~schemas.md#account) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | `account_already_exists` — this login already owns a live account and one account per login is supported today; nothing was created, and repeating the call changes nothing — read the account with `GET /v1/account`. Also the idempotency-conflict codes every mutating `/v1` operation with an `Idempotency-Key` shares: `idempotency_error` (the key was reused with a different body), `idempotency_in_progress` (a request with this key is still running) and `idempotency_failed` (the first attempt answered a 5xx — reconcile, then retry with a new key). | `application/json` [Error](/reference/~schemas.md#error) |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="resolve-the-one-next-action-readiness-kind-available"></a>
## `GET /account/readiness` — Resolve the one next-action readiness kind — available

**Operation ID:** `account.get_readiness`

> **Status:** Available · **Callers:** dashboard session, agent (business key), business key · **Scope:** `account.read` · **Test mode:** unavailable

Resolves the eight-kind readiness machine to the single kind the persistent Today card shows right now — never a list, one kind wins; re-derive on every call. Callers: dashboard session, agent (holder), business key (read-only). Test mode: full. BL-14 (2026-09-15): implemented — server-side mirror of the client's `deriveDashboardReadinessState`, Bridge-only today (Paybis/Transak not yet resolved server-side). On a degraded Bridge read (the last persisted snapshot, published as `Customer.degraded: true`) no fact or sentence says the identity is verified: `accept_terms` then carries only the terms fact, and the card carries `degraded: true`. The kind is resolved exactly as on a live read. `verification=snapshot` builds the card from Swaps' stored Bridge check while it is no more than 24 hours old (no Bridge call); `verification_checked_at` says when the facts were checked, and a live read that cannot reach Bridge answers `503`, never a degraded card.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `VerificationRead` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The resolved readiness kind. | `application/json` [AccountReadiness](/reference/~schemas.md#accountreadiness) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-the-six-section-onboarding-checklist-available"></a>
## `GET /account/setup_guide` — Read the six-section onboarding checklist — available

**Operation ID:** `account.get_setup_guide`

> **Status:** Available · **Callers:** dashboard session, agent (business key), business key · **Scope:** `account.read` · **Test mode:** unavailable

Six sections, eighteen steps (Overview, Wallet, Payment links, Payroll, Pay an invoice, Crypto processing) with per-step and per-section completion and the single next step to surface — replaces the multi-request client-side derivation prod pays for today on every dashboard page. Callers: dashboard session, agent (holder), business key (read-only). Test mode: full. BL-14 (2026-09-15): implemented — `pay_invoice` steps stay permanently undone (no backing resource yet). On a degraded Bridge read (the last persisted snapshot, published as `Customer.degraded: true`) `verify_identity` and `confirm_eligible` report `done: false` and are not counted, and the guide carries `degraded: true`; `next_step` is the step a live read of the same status names, never a step withheld for that reason, and every other step reads as on a live read. `verification=snapshot` builds the verification steps from Swaps' stored Bridge check while it is no more than 24 hours old (no Bridge call); `verification_checked_at` says when the facts were checked, and a live read that cannot reach Bridge answers `503`, never a degraded guide.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `VerificationRead` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The setup guide. | `application/json` [SetupGuide](/reference/~schemas.md#setupguide) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="sign-the-holder-out-of-every-device-planned"></a>
## `POST /account/sessions/revoke_all` — Sign the holder out of every device — planned

**Operation ID:** `account.revoke_all_sessions`

> **Status:** Planned — not built yet · **Callers:** dashboard session · **Scope:** `account.write` · **Test mode:** full
>
> This 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.

The single published exception to the identity-plane carve-out (RESOURCE-MODEL §0.12): every other sign-in, passkey and session concern stays outside the resource contract, but a global revoke is destructive enough to publish. The calling session is not guaranteed to survive it either — expect to re-authenticate. Callers: dashboard session only. Test mode: full.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `204` | Every session is revoked. | — |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-cross-product-activity-ledger-available"></a>
## `GET /activity` — List the cross-product activity ledger — available

**Operation ID:** `activity.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `activity.read` · **Test mode:** unavailable

One row per money object across payment links, payouts, payroll, buy & sell, wallet and crypto processing — the read model over the events outbox (RESOURCE-MODEL §0.12). Today's recent-activity slot is this list's newest three rows, byte for byte. `object{id, type}` points back to the full resource; do not treat `status` as a shared cross-product vocabulary. `status_group` is this resource's own six-bucket set, not shared with payment_links, payouts, payroll_runs or orders — read each of those resources' own `status_group` for their set. K8b moved every `payment_link.*`/`payment.*`/`payout.*` producer off the api-v1 route layer and into the product's own service/action layer (`EventType`'s own description names the full type list) — a dashboard action, a Bridge webhook reducer or a cron sweep now emits too, not only an api-v1-originated mutation. K8c did the same for eight `payroll_run.*` lifecycle types (`created`/`approved`/`funding_verified`/`execution_started`/`completed`/ `partial`/`failed`/`cancelled`), hooked into `payroll/lib.ts`'s own single `appendEvent` choke point — so `payroll` rows now exist here for RUN-level lifecycle transitions, driven by any caller (an employer action, an ops-asserted funding attestation, the funding poll). Payroll ITEM-level events (`payroll_item.*`/`payroll_template.*`), the run's own non-lifecycle audit types (`funding_instructions_*`, `execution_requested`/`.blocked`, `.underfunded`) and payout webhook/RPC-driven states still have no producer outside their own SQL RPCs (a named follow-up). No pre-K8 history is backfilled. `buy_sell`/`wallet`/`crypto_processing` rows do not exist until those products wire their own producers. `product` filters at the query (`resource_type IN (...)`), never by scanning an already-fetched page. Callers: business key, agent, dashboard session. Test mode: fixtures.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |
| `product` | query | no | `payment_links` \| `payouts` \| `payroll` \| `buy_sell` \| `wallet` \| `crypto_processing` | Filters at the query (`resource_type IN (...)`, K8c) — pushed into the underlying read, never applied after a capped fetch. A product with no live producer yet (`buy_sell`, `wallet`, `crypto_processing`) legitimately returns zero rows. |
| `status_group` | query | no | `draft` \| `pending` \| `processing` \| `completed` \| `failed` \| `returned` | This resource's own six buckets (`draft`, `pending`, `processing`, `completed`, `failed`, `returned`) — not the per-product sets `payment_links`, `payouts`, `payroll_runs` and `orders` use for their own `status_group`. |
| `since` | query | no | `string` | — |
| `until` | query | no | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of activity rows. | `application/json` [ActivityList](/reference/~schemas.md#activitylist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-unbounded-counts-across-every-product-available"></a>
## `GET /activity/summary` — Read unbounded counts across every product — available

**Operation ID:** `activity.get_summary`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `activity.read` · **Test mode:** unavailable

Honest, unbounded counts by date window, product and status group — the figures behind the Activity filter chips, covering every row on record, not just the loaded page. A cursor list never returns a total; this is where it lives. `by_product_status` (K8c, §52 C4-D14) crosses product × status_group into one count-and- minor-unit-sum grid for the product hubs and Today tiles; a cell's `amount` is `null` when the cell has no rows or when its rows carry more than one currency — this endpoint never fabricates an FX rate to combine them. Callers: business key, agent, dashboard session. Test mode: fixtures.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The summary. | `application/json` [ActivitySummary](/reference/~schemas.md#activitysummary) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-cross-domain-event-stream-available"></a>
## `GET /events` — List the cross-domain event stream — available

**Operation ID:** `events.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `events.read` · **Test mode:** unavailable

The append-only outbox the product's own service layer writes to for every transition it drives — an api-v1 mutation, a dashboard action, a Bridge webhook reducer or a cron sweep alike (K8b moved the producers off the api-v1 route layer for exactly this reason) — filter by `type` (a dotted event name from RESOURCE-MODEL §3) and `object` (the affected resource's id). Poll this only when a live connection is unavailable; prefer `GET /v1/events/stream` otherwise. `EventType`'s own description names which of the 22 `payment_link.*`/`payment.*`/`payout.*` types (K8) and the eight `payroll_run.*` RUN-level lifecycle types (K8c, §52 C4-D14) currently have a producer; the five `order.*` types also have one now (L5-1) — `_shared/services/api_events.ts`'s `emitOrderOutboxEvent`, wired from the Bridge-native order-create/cancel/status-update paths and the Bridge webhook reducer — and so do the three `customer.*` types (L5-2), the same file's `emitCustomerOutboxEvent`, wired from `bridge-webhook/index.ts`'s single `bridge_customers` writer. See RESOURCE-MODEL §3 for the full writer list. No pre-K8 history is backfilled. Callers: business key, agent, dashboard session. Test mode: unavailable (A4-FIX-6). Test-mode events are not readable through /v1 today — no test object can be created yet; once creates are restored, poll the forwarded per-object `*/events` lists.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |
| `type` | query | no | `string` | A dotted event type, e.g. `payout.settled`. |
| `object` | query | no | `string` | The affected resource's id. |
| `since` | query | no | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of events. | `application/json` [EventList](/reference/~schemas.md#eventlist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-event-by-id-available"></a>
## `GET /events/{id}` — Read one event by id — available

**Operation ID:** `events.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `events.read` · **Test mode:** unavailable

One immutable event, read-only — events are never created through the API. K8b moved every `payment_link.*`/`payment.*`/`payout.*` producer off the api-v1 route layer and into the product's own service/action layer (`EventType`'s own description names the full list) — a dashboard action, a Bridge webhook reducer or a cron sweep emits too, not only an api-v1-originated mutation. No pre-K8 history is backfilled. Callers: business key, agent, dashboard session. Test mode: unavailable (A4-FIX-6). Test-mode events are not readable through /v1 today — no test object can be created yet; once creates are restored, poll the forwarded per-object `*/events` lists.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The event. | `application/json` [Event](/reference/~schemas.md#event) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="subscribe-to-events-over-server-sent-events-available"></a>
## `GET /events/stream` — Subscribe to events over Server-Sent Events — available

**Operation ID:** `events.stream`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `events.read` · **Test mode:** unavailable

The live channel every client should prefer over polling `/v1/events` — same auth, same envelope, one event per SSE frame with `id:` set to the event id. Authenticate exactly as on every other `/v1` call, with the `Authorization` bearer token or `x-api-key` header, sent by opening this connection with `fetch()` against a readable stream — never native browser `EventSource`, which cannot set a header and would force the credential into the URL. A credential in the query string or path is never accepted here, or anywhere else in the API. On a dropped connection, reconnect with the `Last-Event-ID` header set to the last id received; the stream resumes immediately after it, never replaying from the start or skipping ahead. A gap wider than the retention window is not silently bridged — fall back to `GET /v1/events?since=` to recover it. K8b moved every `payment_link.*`/`payment.*`/`payout.*` producer off the api-v1 route layer and into the product's own service/action layer (`EventType`'s own description names the full list) — a dashboard action, a Bridge webhook reducer or a cron sweep emits too, not only an api-v1-originated mutation. No pre-K8 history is backfilled. Callers: business key, agent, dashboard session. Test mode: unavailable — a test-mode caller gets `503 temporarily_unavailable` (side-effect free, no `Retry-After`); the stream is never forwarded to the dev project, because prod could not revalidate the caller's credential on every tick of a forwarded stream (A4-FIX-6). Test-mode events are not readable through /v1 today — no test object can be created yet; once creates are restored, poll the forwarded per-object `*/events` lists.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Last-Event-ID` | header | no | `string` | On reconnect, the id of the last event received — the stream resumes immediately after it. |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | An open `text/event-stream` connection, one `Event` object per frame. | `text/event-stream` `string` |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-accounts-webhook-endpoints-available"></a>
## `GET /webhook_endpoints` — List the account's webhook endpoints — available

**Operation ID:** `webhook_endpoints.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** sandbox

Callers: business key or a first-party bearer session (dashboard/agent). Test mode: sandbox — the request
forwards to the DEV project (K11-6 fixer round 1, finding #7); the endpoint row, its deliveries and the
signing all live there, never in prod.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of webhook endpoints. | `application/json` [WebhookEndpointList](/reference/~schemas.md#webhookendpointlist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="register-a-new-webhook-endpoint-available"></a>
## `POST /webhook_endpoints` — Register a new webhook endpoint — available

**Operation ID:** `webhook_endpoints.create`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Registers an https endpoint and returns its signing secret once, in this response only — a replayed Idempotency-Key gets the same body back with secret redacted to null, never the cleartext twice. 409 when the account already has 20 endpoints (the limit). Callers: business key, or a first-party bearer session (dashboard/agent) whose account_members.role is owner — a non-owner bearer gets 403 scope_denied, the same bar api_keys management uses. Test mode: sandbox — the request forwards to the DEV project (K11-6 fixer round 1, finding #7, corrected from a draft claim that the row is registered in prod); the endpoint row, its deliveries and the signing all live there, never in prod.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [WebhookEndpointCreateRequest](/reference/~schemas.md#webhookendpointcreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The new endpoint, with its secret. | `application/json` [WebhookEndpoint](/reference/~schemas.md#webhookendpoint) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-webhook-endpoint-available"></a>
## `GET /webhook_endpoints/{id}` — Read one webhook endpoint — available

**Operation ID:** `webhook_endpoints.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** sandbox

Never returns `secret` after creation — read the delivery log to verify an endpoint is receiving events. Callers: business key or a first-party bearer session (dashboard/agent). Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — see `webhook_endpoints.list`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The endpoint. | `application/json` [WebhookEndpoint](/reference/~schemas.md#webhookendpoint) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="update-url-event-types-description-or-enabled-state-available"></a>
## `PATCH /webhook_endpoints/{id}` — Update url, event types, description or enabled state — available

**Operation ID:** `webhook_endpoints.update`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Updates the url, subscribed event types, description, or enables/disables the endpoint. Never returns or rotates `secret` — use `rotate_secret` for that. Callers: business key, or a first-party bearer session (dashboard/agent) whose account_members.role is owner — a non-owner bearer gets 403 scope_denied, the same bar api_keys management uses (a bearer who could repoint `url` to a host they control would receive the account's signed deliveries). Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — see `webhook_endpoints.list`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [WebhookEndpointUpdateRequest](/reference/~schemas.md#webhookendpointupdaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The updated endpoint. | `application/json` [WebhookEndpoint](/reference/~schemas.md#webhookendpoint) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="remove-a-webhook-endpoint-available"></a>
## `DELETE /webhook_endpoints/{id}` — Remove a webhook endpoint — available

**Operation ID:** `webhook_endpoints.delete`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Deletes the endpoint and stops all future deliveries to it; past deliveries stay in the log. Callers: business key, or a first-party bearer session (dashboard/agent) whose account_members.role is owner — a non-owner bearer gets 403 scope_denied, the same bar api_keys management uses. Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — see `webhook_endpoints.list`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `204` | Deleted. | — |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="rotate-the-endpoints-signing-secret-available"></a>
## `POST /webhook_endpoints/{id}/rotate_secret` — Rotate the endpoint's signing secret — available

**Operation ID:** `webhook_endpoints.rotate_secret`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Issues a new signing secret and keeps the OLD one live for a 24h overlap window — every delivery in that window is signed with BOTH keys, so a receiver still configured with the old secret keeps validating. Returns the new secret once, in this response only, exactly like `create` (redacted from a replayed Idempotency-Key's stored body). Callers: business key, or a first-party bearer session (dashboard/agent) whose account_members.role is owner — a non-owner bearer gets 403 scope_denied, the same bar api_keys management uses. Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — see `webhook_endpoints.list`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The endpoint, with its new secret. | `application/json` [WebhookEndpoint](/reference/~schemas.md#webhookendpoint) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="send-a-synthetic-test-event-to-this-endpoint-available"></a>
## `POST /webhook_endpoints/{id}/send_test_event` — Send a synthetic test event to this endpoint — available

**Operation ID:** `webhook_endpoints.send_test_event`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Emits one `test.ping` event and enqueues exactly one delivery, to this endpoint only — never fanned out to any other endpoint, even one that also subscribes to `test.ping` (or subscribes to "all" via an empty `event_types`). The event's `livemode` matches the caller's own key/session mode. 409 when the endpoint is disabled; 503 while api_v1.webhooks_delivery itself is off (queuing a test would sit forever with nothing to process it). Callers: business key, or a first-party bearer session (dashboard/agent) whose account_members.role is owner — gated the same way as create/update/rotate_secret/delete, for consistency: a non-owner bearer gets 403 scope_denied. Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — the queued delivery must be signed and sent by the DEV project's api-webhooks-worker, or the test ping never fires.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `202` | The queued test delivery. | `application/json` [WebhookDelivery](/reference/~schemas.md#webhookdelivery) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-webhook-delivery-attempts-available"></a>
## `GET /webhook_deliveries` — List webhook delivery attempts — available

**Operation ID:** `webhook_deliveries.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** sandbox

Every delivery attempted or scheduled across this account's endpoints, newest first — filter by `endpoint_id` or `status`. Callers: business key or a first-party bearer session (dashboard/agent). Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — a test caller's deliveries only ever exist on the DEV project, written by its api-webhooks-worker.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |
| `endpoint_id` | query | no | `string` | — |
| `status` | query | no | `pending` \| `succeeded` \| `failed` \| `exhausted` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of deliveries. | `application/json` [WebhookDeliveryList](/reference/~schemas.md#webhookdeliverylist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-webhook-delivery-available"></a>
## `GET /webhook_deliveries/{id}` — Read one webhook delivery — available

**Operation ID:** `webhook_deliveries.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** sandbox

Callers: business key or a first-party bearer session (dashboard/agent). Test mode: sandbox (K11-6, §4.2
drift, was declared `full`) — see `webhook_deliveries.list`.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The delivery. | `application/json` [WebhookDelivery](/reference/~schemas.md#webhookdelivery) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="replay-a-webhook-delivery-available"></a>
## `POST /webhook_deliveries/{id}/replay` — Replay a webhook delivery — available

**Operation ID:** `webhook_deliveries.replay`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.write` · **Test mode:** sandbox

Enqueues a NEW delivery for the same event, to the same endpoint — a fresh `WebhookDelivery` row, distinct from (and never mutating) the one replayed; the original stays in the log exactly as it was. Safe to call repeatedly. 409 when the endpoint is currently disabled, or when its CURRENT event_types no longer include this event's type. Callers: business key or a first-party bearer session (dashboard/agent). Test mode: sandbox (K11-6, §4.2 drift, was declared `full`) — the new delivery it enqueues is signed and sent by the DEV project's api-webhooks-worker, same as the original.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The new delivery, queued. | `application/json` [WebhookDelivery](/reference/~schemas.md#webhookdelivery) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="join-the-webhook-delivery-waitlist-available"></a>
## `POST /webhook_waitlist` — Join the webhook-delivery waitlist — available

**Operation ID:** `webhook_waitlist.create`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `account.write` · **Test mode:** unavailable

Captures an email ahead of the full webhook system existing — the "Join the waitlist" action on the Developers hub. Callers: business key or a first-party bearer session (dashboard/agent). Test mode: full.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [WebhookWaitlistRequest](/reference/~schemas.md#webhookwaitlistrequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `202` | Captured. | — |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="join-the-card-waitlist-available"></a>
## `POST /card_waitlist` — Join the card waitlist — available

**Operation ID:** `card_waitlist.create`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `account.write` · **Test mode:** unavailable

Write-only capture ahead of a card product existing — no corresponding read and no card-issuing machinery behind this call today. Callers: business key, agent, dashboard session (A1-2: widened to match the router's own `BOTH_CALLERS` entry — the security block below, `businessKey`, was already the truthful half). Test mode: full.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [CardWaitlistRequest](/reference/~schemas.md#cardwaitlistrequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `202` | Captured. | — |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-saved-payment-destinations-available"></a>
## `GET /address_book` — List saved payment destinations — available

**Operation ID:** `address_book.list`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.read` · **Test mode:** unavailable

Every saved destination across all seven rail shapes — crypto and six bank rails. Bank-rail fields are masked on every read regardless of caller. Callers: agent (holder), dashboard session — never a business key (review ruling P1-2: no account-level address book exists yet, so a business-key read would show one member's saved destinations to any other caller holding a key for the same account). Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of saved destinations. | `application/json` [AddressBookList](/reference/~schemas.md#addressbooklist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="save-a-new-payment-destination-available"></a>
## `POST /address_book` — Save a new payment destination — available

**Operation ID:** `address_book.create`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.write` · **Test mode:** unavailable

Saves one of seven rail shapes, discriminated by `rail`. Bank-rail details cross the boundary once here and are masked on every subsequent read — most clients log tool arguments verbatim. Callers: agent (holder), dashboard session, never a business key. Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [AddressBookEntryCreateRequest](/reference/~schemas.md#addressbookentrycreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The saved destination. | `application/json` [AddressBookEntry](/reference/~schemas.md#addressbookentry) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-saved-destination-available"></a>
## `GET /address_book/{id}` — Read one saved destination — available

**Operation ID:** `address_book.get`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.read` · **Test mode:** unavailable

Callers: agent (holder), dashboard session — never a business key (review ruling P1-2, same reasoning as
`address_book.list`). Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The destination. | `application/json` [AddressBookEntry](/reference/~schemas.md#addressbookentry) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="rename-a-saved-destination-available"></a>
## `PATCH /address_book/{id}` — Rename a saved destination — available

**Operation ID:** `address_book.update`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.write` · **Test mode:** unavailable

Updates the label only — every rail field is immutable after creation; remove and re-add to change a destination. Callers: agent (holder), dashboard session, never a business key. Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [AddressBookEntryUpdateRequest](/reference/~schemas.md#addressbookentryupdaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The updated destination. | `application/json` [AddressBookEntry](/reference/~schemas.md#addressbookentry) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="remove-a-saved-destination-available"></a>
## `DELETE /address_book/{id}` — Remove a saved destination — available

**Operation ID:** `address_book.delete`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.write` · **Test mode:** unavailable

Callers: agent (holder), dashboard session, never a business key. Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `204` | Deleted. | — |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="re-screen-a-saved-crypto-address-available"></a>
## `POST /address_book/{id}/recheck` — Re-screen a saved crypto address — available

**Operation ID:** `address_book.recheck`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.write` · **Test mode:** unavailable

Re-runs a free screening against a saved crypto destination on its own `network` and returns the entry carrying that fresh screening and its `last_checked_at` — a `200` never answers `screening: null` for a crypto entry; a no-op on a bank-rail entry. An entry whose network screening does not cover (Tempo, for one), or whose address cannot exist on it, is refused `409 network_not_supported` before anything is screened or recorded — never screened as Ethereum. Callers: agent (holder), dashboard session, never a business key. Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The destination, with a refreshed screening. | `application/json` [AddressBookEntry](/reference/~schemas.md#addressbookentry) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | `capability_unavailable` — code `network_not_supported`: screening does not cover the entry's `network` (Tempo, Litecoin, Dogecoin, any name outside the networks `ScreeningCreateRequest.chain` lists) or the address cannot exist on it; nothing was screened or recorded and the entry is unchanged — do not retry. Also the idempotency-conflict codes every mutating `/v1` operation with an `Idempotency-Key` shares: `idempotency_error` (the key was reused with a different body), `idempotency_in_progress` (a request with this key is still running) and `idempotency_failed` (the first attempt answered a 5xx — reconcile, then retry with a new key). | `application/json` [Error](/reference/~schemas.md#error) |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="update-the-travel-rule-beneficiary-counterparty-details-for-a-saved-destination-available"></a>
## `POST /address_book/{id}/beneficiary` — Update the Travel Rule beneficiary/counterparty details for a saved destination — available

**Operation ID:** `address_book.beneficiary.update`

> **Status:** Available · **Callers:** agent (business key), dashboard session · **Scope:** `address_book.write` · **Test mode:** unavailable

Drift blocker from the 28.09 v1→v2 audit (DRIFT-v1-v2-2026-09-28.md item 1) — mirrors the dashboard's Manage tab (`CounterpartyForm`/`CounterpartyModal`), the actual v1 write path for these fields (a direct `address_book` update, not the separate append-only `travel_rule_attestations` audit log, which is stamped only mid-transfer, by `attestTransfer`, with originator KYC context and a `transaction_id` this operation does not have). Sets `beneficiary_type` to `third-party` and stores `beneficiary_subtype` + the counterparty details; it never writes `travel_rule_last_attested_at` (fixer round 1 #1) — that column stays read-only here and is stamped only by the transfer-time attestation. `counterparty_*` are readable on every read (fixer round 1 #2), so a Manage-tab re-save can pre-fill from them. Proof-of-control verification is a separate, out-of-scope flow; `proof_of_control_*` on the response reflect the entry's existing state, untouched by this call. Callers: agent (holder), dashboard session, never a business key — same reasoning as every other `address_book` write. Test mode: fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [TravelRuleAttestationCreateRequest](/reference/~schemas.md#travelruleattestationcreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The destination, with the beneficiary details updated. | `application/json` [AddressBookEntry](/reference/~schemas.md#addressbookentry) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-the-holders-credit-balance-available"></a>
## `GET /credits` — Read the holder's credit balance — available

**Operation ID:** `credits.get`

> **Status:** Available · **Callers:** dashboard session, agent (business key) · **Scope:** `credits.read` · **Test mode:** unavailable

Free and paid balances plus a lifetime check count — the figures behind every credit-gated screen in Tools and Settings. Callers: dashboard session, agent (holder) — never a business key (review ruling P1-2: no account-level credit ledger exists yet, so this resolves the account OWNER's personal balance; a business-key read would show it to any other caller holding a key for the same account). Test mode: `unavailable` (K11-6 fixer round 1, finding #6) — the ledger is billed through Stripe and there is no test purchase to read against yet (`credit_checkouts.create`, design §9 D-4), so this read carries no restored test-mode claim.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The balance. | `application/json` [Credits](/reference/~schemas.md#credits) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-credit-ledger-available"></a>
## `GET /credit_events` — List the credit ledger — available

**Operation ID:** `credit_events.list`

> **Status:** Available · **Callers:** dashboard session, agent (business key) · **Scope:** `credits.read` · **Test mode:** unavailable

One of seven event types per row (`consume_free`, `consume_paid`, `cache_hit`, `insufficient`, `purchase`, `refund`, `admin_grant`), newest first, including the screened `address`/`chain` on consume rows. Callers: dashboard session, agent (holder) — never a business key (review ruling P1-2, same reasoning as `credits.get`). Test mode: `unavailable` (K11-6 fixer round 1, finding #6) — same reasoning as `credits.get` above.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of ledger rows. | `application/json` [CreditEventList](/reference/~schemas.md#crediteventlist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="start-a-stripe-checkout-for-a-credit-pack-available"></a>
## `POST /credit_checkouts` — Start a Stripe checkout for a credit pack — available

**Operation ID:** `credit_checkouts.create`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `credits.write` · **Test mode:** unavailable

Creates a Stripe Checkout session for one of the three credit packs (5, 10 or 20) and returns its url — redirect the holder there; this call never itself grants credits. Callers: dashboard session, agent (holder). Test mode: unavailable (K11-6, §4.2 drift, corrects an earlier "Stripe test mode" claim) — it opens a real payment for credits and there is no test purchase; refused pending the founder's decision (design D-4). `credits.get`/ `credit_events.list` are unaffected — they read the account's own credit ledger, not this checkout.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [CreditCheckoutRequest](/reference/~schemas.md#creditcheckoutrequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The checkout session. | `application/json` [CreditCheckout](/reference/~schemas.md#creditcheckout) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-accounts-api-keys-available"></a>
## `GET /api_keys` — List the account's API keys — available

**Operation ID:** `api_keys.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** full

Never returns `secret` — only `create` and `roll` ever do, once each. Callers: dashboard session or business key (read-only) — `create`/`roll`/`revoke` are never a business key, only dashboard session. Test mode: full (K11-4) — credentials and the outer usage log are prod-only by design (§2.3), so a `livemode=false` caller's own test key(s) answer from the same real row this operation always read, scoped to the caller's test twin account; a live caller's keys and a test caller's are never each other's to see.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of keys. | `application/json` [ApiKeyList](/reference/~schemas.md#apikeylist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="create-a-new-api-key-available"></a>
## `POST /api_keys` — Create a new API key — available

**Operation ID:** `api_keys.create`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `developers.write` · **Test mode:** full

Issues a key through the create_api_key_for_account RPC (K10) and returns its plaintext secret once, in this response only — a replayed Idempotency-Key gets the same body back with secret redacted to null, never the cleartext twice (the same mechanism K9 introduced for webhook_endpoints.create). Callers: dashboard session only — a key can never mint another key. Test mode: full (K11-4) — `livemode:false` resolves-or-creates the caller's test twin account (a second, `livemode=false` account row idempotently linked to the caller's own) and mints the `sk_test_` key on the twin, never on the caller's live account; this is the one call site a `livemode=false` account can be created from.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [ApiKeyCreateRequest](/reference/~schemas.md#apikeycreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The new key, with its secret. | `application/json` [ApiKeyCreated](/reference/~schemas.md#apikeycreated) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-api-key-available"></a>
## `GET /api_keys/{id}` — Read one API key — available

**Operation ID:** `api_keys.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** full

Callers: dashboard session or business key (read-only) — `create`/`roll`/`revoke` are never a business key, only dashboard session. Test mode: full (K11-4) — scoped by `account_id`, which for a test caller is the twin account, never the live one.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The key. | `application/json` [ApiKey](/reference/~schemas.md#apikey) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="rotate-a-keys-secret-available"></a>
## `POST /api_keys/{id}/roll` — Rotate a key's secret — available

**Operation ID:** `api_keys.roll`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `developers.write` · **Test mode:** full

Issues a new secret for the same key identity (via the roll_api_key RPC) and invalidates the old one; the new secret is returned once, in this response only — a replayed Idempotency-Key gets the same body back with secret redacted to null, never the cleartext twice. Callers: dashboard session only. Test mode: full (K11-4) — rolls a key already owned by the caller's own account (live or twin); never resolves a twin itself.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The key, with its new secret. | `application/json` [ApiKeyCreated](/reference/~schemas.md#apikeycreated) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="revoke-a-key-permanently-available"></a>
## `POST /api_keys/{id}/revoke` — Revoke a key permanently — available

**Operation ID:** `api_keys.revoke`

> **Status:** Available · **Callers:** dashboard session · **Scope:** `developers.write` · **Test mode:** full

Sets `status: revoked` and `revoked_at`; every request against the key then fails `authentication_error`. Irreversible — issue a new key instead of expecting an un-revoke. Callers: dashboard session only. Test mode: full (K11-4) — revokes a key already owned by the caller's own account (live or twin).

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The revoked key. | `application/json` [ApiKey](/reference/~schemas.md#apikey) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-keys-today-only-usage-available"></a>
## `GET /api_keys/{id}/usage` — Read one key's today-only usage — available

**Operation ID:** `api_keys.get_usage`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** full

Today-only counters — no trend history exists yet; use `/v1/request_logs` for the per-call detail behind these counts. Callers: dashboard session or business key (read-only). Test mode: full (K11-4) — the caller's own account's usage (live or twin), never the other's.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | Today's usage. | `application/json` [ApiKeyUsage](/reference/~schemas.md#apikeyusage) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-raw-per-call-telemetry-for-own-keys-available"></a>
## `GET /request_logs` — List raw per-call telemetry for own keys — available

**Operation ID:** `request_logs.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `developers.read` · **Test mode:** full

Raw per-call HTTP telemetry across the caller's own keys — request id, operation, status code, latency and error code — distinct from the product-lifecycle `/v1/events` catalogue. Callers: dashboard session or business key (read-only), scoped to the caller's own keys. Test mode: full (K11-4) — the usage log of the caller's own account's keys (live or twin), never the other's; this is the outer leg's own log, kept in prod by design (§2.3), not the per-operation `/v1/events` catalogue a test caller's dev-side calls emit into.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of request logs. | `application/json` [RequestLogList](/reference/~schemas.md#requestloglist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |
