# Confirmations: agent integration guide

Confirmations lets merchants register their own cold-wallet addresses and create
exact-amount payment intents. It watches on-chain transfers to those addresses and
POSTs a webhook after confirmation. Funds go directly to the recipient;
Confirmations never custodies funds or needs private keys. Public donation
challenges and donation signup send funds directly to the EFF.

Intended production base URL: `https://api.confirmations.info`. For self-hosting,
use your own host. Examples use `$BASE`; set `$KEY` to your returned API key.
Amounts, IDs and timestamps below are illustrative; JSON field names and types
match the API. Always pay the exact amount returned by your own request.

```sh
BASE=https://api.confirmations.info
KEY=YOUR_API_KEY
```

## Authentication and discovery

Send `X-API-Key: $KEY` on endpoints tagged `addresses`, `payments`, or `account`.
Endpoints tagged `public`, `challenges`, and `agents` need no authentication.
Every response carries a generated UUID in `X-Request-Id`.

```sh
curl -s "$BASE/plans"
curl -s "$BASE/signup/options"
```

`GET /plans` returns an array of `PlanOut` objects:
```json
[{"name":"starter","monthly_event_limit":100,"monthly_price_usd":0},{"name":"growth","monthly_event_limit":10000,"monthly_price_usd":49},{"name":"scale","monthly_event_limit":100000,"monthly_price_usd":199}]
```

Default tiers: starter is free with 100 events/month and a $5 minimum donation;
growth is $49/month with 10,000 events/month and a $49 minimum donation;
scale is $199/month with 100,000 events/month and a $199 minimum donation.
The starter minimum is configurable. `GET /signup/options` returns
`SignupOptionsOut` (nested `SignupOptionsChain` and `SignupOptionsPlan`):
```json
{"min_donation_usd":5,"chains":[{"chain":"bitcoin","address":"3LTu6uavQ4A3kgDauZipyGqcHQEUSVe2so","symbol":"BTC","auto_verify":true},{"chain":"ethereum","address":"0x1ca9EB2a5C213d417269134b80111F57e1644105","symbol":"ETH","auto_verify":true},{"chain":"binance","address":"0x375d784041BD851D68Ddd7719646e52C80c6F496","symbol":"BNB","auto_verify":true}],"plans":[{"name":"starter","monthly_event_limit":100,"monthly_price_usd":0,"min_donation_usd":5},{"name":"growth","monthly_event_limit":10000,"monthly_price_usd":49,"min_donation_usd":49},{"name":"scale","monthly_event_limit":100000,"monthly_price_usd":199,"min_donation_usd":199}]}
```

Merchant address/payment chains: `bitcoin`, `ethereum`, `base`, `arbitrum`,
`optimism`, `polygon`, `bnb`, `avalanche`, `linea`, `zksync`.
Donation signup and challenges accept only `bitcoin`, `ethereum`, `binance`;
`binance` is the donation API name for the chain named `bnb` in merchant APIs.

## 1. Obtain an API key

`SignupIn`: `method` is required (`donation` or `subscribe`); `chain`,
`usd_amount`, and `email` default to null; `plan` defaults to `starter`.
When supplied, `usd_amount` must be positive. Donation requires chain and amount.
Subscribe requires `growth` or `scale` and immediately creates an active account;
the current endpoint does not collect billing details or process a charge.

Donation request (`SignupIn`):
```sh
curl -s -X POST "$BASE/signup" -H 'Content-Type: application/json' \
  -d '{"method":"donation","chain":"ethereum","usd_amount":"5.00","plan":"starter","email":"ops@example.com"}'
```
Response (`DonationSignupOut`, with nested `DonationIntentOut`; key is illustrative):
```json
{"api_key":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","user_id":"22222222-2222-4222-8222-222222222222","plan":"starter","account_status":"pending","donation":{"intent_id":"11111111-1111-4111-8111-111111111111","chain":"ethereum","address":"0x1ca9EB2a5C213d417269134b80111F57e1644105","amount_native":"0.002","amount_smallest":"2000000000000000","usd_amount":"5.00","expires_at":"2026-09-09T13:00:00+00:00"}}
```
Store the API key securely now: the plaintext key cannot be recovered. It works
immediately for account views and address registration/listing. The subscription
starts `pending`; `POST /payments` returns 403 `account_pending` until activation.
Send exactly `donation.amount_native` to `donation.address` before expiry
(default `SIGNUP_INTENT_TTL=3600` seconds), then poll:
```sh
INTENT_ID=11111111-1111-4111-8111-111111111111
curl -s "$BASE/signup/$INTENT_ID"
```
Response (`IntentStatusOut`):
```json
{"intent_id":"11111111-1111-4111-8111-111111111111","status":"PENDING","account_status":"pending","chain":"ethereum","amount_native":"0.002","tx_hash":null,"expires_at":"2026-09-09T13:00:00+00:00"}
```
Signup intent statuses: `PENDING`, `CONFIRMED`, `EXPIRED`.
Confirmation activates the existing subscription and changes `account_status` to
`active`; use the same API key. Expiry leaves the subscription `pending` and the
key valid. Older intents without a linked subscription report `pending`.

Alternatively, subscribe directly (`SignupIn`):
```sh
curl -s -X POST "$BASE/signup" -H 'Content-Type: application/json' \
  -d '{"method":"subscribe","chain":null,"usd_amount":null,"plan":"growth","email":"ops@example.com"}'
```
Response (`SignupOut`):
```json
{"user_id":"22222222-2222-4222-8222-222222222222","api_key":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","plan":"growth","account_status":"active"}
```

## 2. Register your own receive addresses

Request (`AddAddressesIn`), then list:
```sh
curl -s -X POST "$BASE/addresses" -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"chain":"ethereum","addresses":["0x1111111111111111111111111111111111111111"]}'
curl -s "$BASE/addresses" -H "X-API-Key: $KEY"
```
Both return arrays of `AddressOut`:
```json
[{"chain":"ethereum","address":"0x1111111111111111111111111111111111111111"}]
```
Registration is idempotent by user, chain, address. Replace the example with your
own cold-wallet address. Optional cleanup after finishing payments: remove an
address, optionally filtered by chain (skip this command while following the flow):
```sh
curl -s -X DELETE "$BASE/addresses/0x1111111111111111111111111111111111111111?chain=ethereum" -H "X-API-Key: $KEY"
```
Response:
```json
{"deleted":"0x1111111111111111111111111111111111111111"}
```

## 3. Create and track a payment

Request (`CreatePaymentIn`; all three fields required):
```sh
curl -s -X POST "$BASE/payments" -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"usd_amount":"25.00","chain":"ethereum","callback_url":"https://shop.example/callback"}'
```
Response (`CreatePaymentOut`):
```json
{"payment_id":"33333333-3333-4333-8333-333333333333","chain":"ethereum","address":"0x1111111111111111111111111111111111111111","amount_wei":"10483000000000000","amount_native":"0.010483","usd_amount":"25.00","expires_at":"2026-09-09T13:00:00+00:00"}
```
The service allocates an address from your pool. Have the payer send the exact
returned amount, without rounding. Despite its name, `amount_wei` is the smallest
native unit: satoshis on Bitcoin, wei on EVM chains. Amounts are strings to avoid
floating-point precision loss. Payment TTL defaults to 3600 seconds. Creating a
payment consumes one event; an active subscription and registered address are required.

```sh
PAYMENT_ID=33333333-3333-4333-8333-333333333333
curl -s "$BASE/payments/$PAYMENT_ID" -H "X-API-Key: $KEY"
curl -s "$BASE/payments" -H "X-API-Key: $KEY"
```
Single response (`PaymentStatus`); list returns an array of these, newest first:
```json
{"payment_id":"33333333-3333-4333-8333-333333333333","status":"PENDING","chain":"ethereum","address":"0x1111111111111111111111111111111111111111","amount_native":"0.010483","usd_amount":"25.00","tx_hash":null,"confirmations":0}
```
Payment statuses: `PENDING`, `PAID`, `EXPIRED`. `tx_hash` becomes a string once
detected. The payment confirmation threshold defaults to `CONFIRMATIONS_REQUIRED=2`.

## 4. Receive callbacks

Payment callback is a JSON POST to your `callback_url`:
```json
{"paymentId":"33333333-3333-4333-8333-333333333333","userId":"22222222-2222-4222-8222-222222222222","chain":"ethereum","address":"0x1111111111111111111111111111111111111111","txHash":"0xabc123","blockNumber":23000000,"confirmations":2,"expectedAmountWei":"10483000000000000"}
```
Respond with 2xx. Delivery retries on other statuses or exceptions with exponential
backoff starting at 1 second, doubling, capped at 30 seconds. Defaults:
`CALLBACK_MAX_RETRIES=6` total attempts and `CALLBACK_TIMEOUT_SECONDS=10` per attempt.
After exhaustion delivery is logged and dropped (no durable retry queue); poll
status to reconcile. Handle repeated callbacks idempotently by payment/challenge ID.

## Account and subscription

```sh
curl -s "$BASE/account" -H "X-API-Key: $KEY"
curl -s -X POST "$BASE/account/subscribe" -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' -d '{"plan":"growth"}'
```
`SubscribeIn` contains only required `plan` (`starter`, `growth`, `scale`).
Both endpoints return `AccountOut` (example after subscribing):
```json
{"user_id":"22222222-2222-4222-8222-222222222222","email":"ops@example.com","plan":"growth","status":"active","events_used":1,"monthly_event_limit":10000,"period_end":"2026-10-09T12:00:00+00:00","pending_donation":null}
```
`pending_donation` contains the newest PENDING `DonationIntentOut` for the user,
or null when none exists (including after confirmation or expiry). Fresh donation
accounts have `status="pending"`. `POST /account/subscribe` also activates pending
accounts immediately, while preserving existing usage and period dates. A still
PENDING intent remains in `pending_donation` even after this activation.
`email` can be null. Without a subscription, `plan`, `status`, `events_used`,
`monthly_event_limit`, and `period_end` are all null. Billing periods use 30 days.

## Public donation challenges

No API key or account needed. Request (`ChallengeIn`):
```sh
curl -s -X POST "$BASE/challenges" -H 'Content-Type: application/json' \
  -d '{"chain":"ethereum","usd_amount":"1.00","callback_url":"https://shop.example/challenge-callback"}'
```
`chain` and positive `usd_amount` are required; `callback_url` defaults to null.
Default minimum is `CHALLENGE_MIN_USD=1`; TTL is `CHALLENGE_TTL=3600` seconds.
Response (`ChallengeOut`):
```json
{"challenge_id":"44444444-4444-4444-8444-444444444444","chain":"ethereum","address":"0x1ca9EB2a5C213d417269134b80111F57e1644105","amount_native":"0.0004","amount_smallest":"400000000000000","usd_amount":"1.00","expires_at":"2026-09-09T13:00:00+00:00"}
```
Pay exactly the returned amount to EFF, then poll or await the optional callback:
```sh
CHALLENGE_ID=44444444-4444-4444-8444-444444444444
curl -s "$BASE/challenges/$CHALLENGE_ID"
```
Response (`ChallengeStatusOut`):
```json
{"challenge_id":"44444444-4444-4444-8444-444444444444","status":"PENDING","chain":"ethereum","amount_native":"0.0004","tx_hash":null,"expires_at":"2026-09-09T13:00:00+00:00"}
```
Challenge statuses: `PENDING`, `CONFIRMED`, `EXPIRED`. Signup donations and challenges
are confirmed when the monitor matches a mined transfer; they do not use the
merchant payment confirmation threshold. Challenge callback payload:
```json
{"challengeId":"44444444-4444-4444-8444-444444444444","chain":"ethereum","address":"0x1ca9EB2a5C213d417269134b80111F57e1644105","txHash":"0xabc123","expectedAmount":"400000000000000","amountNative":"0.0004","usdAmount":"1.00"}
```

## Errors and references

HTTP exceptions and request-validation failures use this JSON envelope:
```json
{"error":{"code":"unauthorized","message":"Invalid API key","hint":null}}
```
`code` and `message` are strings; `hint` is a string or null. For validation errors,
`hint` contains the full validation error list serialized as a JSON string.
Default codes: 400 `bad_request`, 401 `unauthorized`, 403 `forbidden`,
404 `not_found`, 409 `conflict`, 422 `validation_error`, 429 `too_many_requests`,
502 `upstream_error`, 503 `service_unavailable`; other HTTP exceptions use `error`.
Explicit structured exception details may supply their own code/message/hint.
Pending accounts receive HTTP 403 from `POST /payments`:
```json
{"error":{"code":"account_pending","message":"Account is not active yet","hint":"Complete the EFF donation returned at signup, then retry. Check GET /signup/{intent_id}."}}
```

`GET /health` checks only the local database and monitor flag; no external API call.
A successful database query returns HTTP 200 with this shape:
```json
{"status":"ok","version":"1.1.0","database":"ok","monitor":"running","time":"2026-09-09T12:00:00+00:00"}
```
The monitor field is `running` or `stopped`; it is informational and does not
change the status code. A database failure returns HTTP 503 with `status` set to
`degraded` and `database` set to `error`, preserving the other fields.
See [exhaustive endpoint reference](/llms-full.txt), [OpenAPI JSON](/openapi.json),
and [interactive API documentation](/docs).


# Exhaustive endpoint reference

Generated from the current OpenAPI schema.

## GET /account

Get account

Returns the caller's subscription plan, usage, billing period, and pending donation.

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Account identity, subscription, and current event usage.
  - `pending_donation`: any (required) — Newest pending signup donation instructions, otherwise null
    - `anyOf 1`: object (optional) — Exact donation instructions for a pending signup intent.
      - `intent_id`: string (required) — UUID identifying the signup intent
      - `chain`: string (required) — Chain identifier
      - `address`: string (required) — Receive wallet address
      - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
      - `amount_smallest`: string (required) — Exact amount in smallest units (wei or satoshis), as a string
      - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
      - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp
    - `anyOf 2`: null (optional)
  - `user_id`: string (required) — UUID identifying the account
  - `email`: any (required) — Optional account contact email
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `plan`: any (required) — Current plan name
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `status`: any (required) — Subscription status
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `events_used`: any (required) — Events consumed this billing period
    - `anyOf 1`: integer (optional)
    - `anyOf 2`: null (optional)
  - `monthly_event_limit`: any (required) — Maximum payment events per month
    - `anyOf 1`: integer (optional)
    - `anyOf 2`: null (optional)
  - `period_end`: any (required) — ISO 8601 end of current billing period
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## POST /account/subscribe

Subscribe to plan

Create or change the caller's subscription plan. Plans: starter (free, 100 events/mo), growth ($49, 10k events/mo), scale ($199, 100k events/mo).

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

Media type: `application/json`

- `body`: object (required) — Choose the account subscription plan.
  - `plan`: string (required) — Plan name: starter, growth, or scale

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Account identity, subscription, and current event usage.
  - `pending_donation`: any (required) — Newest pending signup donation instructions, otherwise null
    - `anyOf 1`: object (optional) — Exact donation instructions for a pending signup intent.
      - `intent_id`: string (required) — UUID identifying the signup intent
      - `chain`: string (required) — Chain identifier
      - `address`: string (required) — Receive wallet address
      - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
      - `amount_smallest`: string (required) — Exact amount in smallest units (wei or satoshis), as a string
      - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
      - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp
    - `anyOf 2`: null (optional)
  - `user_id`: string (required) — UUID identifying the account
  - `email`: any (required) — Optional account contact email
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `plan`: any (required) — Current plan name
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `status`: any (required) — Subscription status
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `events_used`: any (required) — Events consumed this billing period
    - `anyOf 1`: integer (optional)
    - `anyOf 2`: null (optional)
  - `monthly_event_limit`: any (required) — Maximum payment events per month
    - `anyOf 1`: integer (optional)
    - `anyOf 2`: null (optional)
  - `period_end`: any (required) — ISO 8601 end of current billing period
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)

#### 400: Unknown plan

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /addresses

List addresses

Returns all wallet addresses registered by the caller.

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: array (required)
  - `items`: object (optional) — A registered receive address and its chain.
    - `chain`: string (required) — Chain identifier
    - `address`: string (required) — Receive wallet address

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## POST /addresses

Register addresses

Register one or more wallet addresses for a chain. Duplicates are idempotent by (user, chain, address).

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

Media type: `application/json`

- `body`: object (required) — Register merchant-owned receive addresses on one chain.
  - `chain`: string (required) — Target chain: bitcoin, ethereum, base, arbitrum, optimism, polygon, bnb, avalanche, linea, zksync
  - `addresses`: array (required) — Wallet addresses to register
    - `items`: string (optional)

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: array (required)
  - `items`: object (optional) — A registered receive address and its chain.
    - `chain`: string (required) — Chain identifier
    - `address`: string (required) — Receive wallet address

#### 400: Unsupported chain

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## DELETE /addresses/{address}

Remove address

Delete an address. Optionally filter by chain to remove a single (chain, address) record.

Authentication: Required (X-API-Key header).

### Parameters

- `path address`: string (required)
- `query chain`: any (optional)
  - `anyOf 1`: string (optional)
  - `anyOf 2`: null (optional)
- `header x-api-key`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: any (required)

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Address not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## POST /challenges

Create donation challenge

Create a donation challenge. The caller's service gates an action behind this small
charitable donation to the EFF. Poll the challenge status or provide a callback URL.

Authentication: Not required.

### Parameters

None.

### Request body

Media type: `application/json`

- `body`: object (required) — Create an exact-amount EFF donation challenge.
  - `chain`: string (required) — Donation chain: bitcoin, ethereum, or binance
  - `usd_amount`: any (required) — USD amount to donate
    - `anyOf 1`: number (optional)
    - `anyOf 2`: string (optional)
  - `callback_url`: any (optional) — Optional URL to receive webhook on confirmation
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Donation instructions for a newly created challenge.
  - `challenge_id`: string (required) — UUID identifying the donation challenge
  - `chain`: string (required) — Chain identifier
  - `address`: string (required) — Receive wallet address
  - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
  - `amount_smallest`: string (required) — Exact amount in smallest units (wei or satoshis), as a string
  - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
  - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp

#### 400: Invalid parameters

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Failed to fetch price

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /challenges/{challenge_id}

Get challenge status

Returns the current status of a donation challenge.

Authentication: Not required.

### Parameters

- `path challenge_id`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Current challenge status and observed transaction information.
  - `challenge_id`: string (required) — UUID identifying the donation challenge
  - `status`: string (required) — PENDING, CONFIRMED, or EXPIRED
  - `chain`: string (required) — Chain identifier
  - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
  - `tx_hash`: any (required) — Transaction hash once detected, otherwise null
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Challenge not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /health

Check service health

Check the database locally; monitor state is informational. No external API calls.

Authentication: Not required.

### Parameters

None.

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Local database readiness and monitor initialization status.
  - `status`: string (required, enum=["ok", "degraded"]) — Overall database readiness
  - `version`: string (required) — Application version
  - `database`: string (required, enum=["ok", "error"]) — Result of a local SELECT 1 query
  - `monitor`: string (required, enum=["running", "stopped"]) — Whether the monitor has been initialized
  - `time`: string (required) — ISO 8601 UTC timestamp of the health check

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Database unavailable

Media type: `application/json`

- `body`: object (required) — Local database readiness and monitor initialization status.
  - `status`: string (required, enum=["ok", "degraded"]) — Overall database readiness
  - `version`: string (required) — Application version
  - `database`: string (required, enum=["ok", "error"]) — Result of a local SELECT 1 query
  - `monitor`: string (required, enum=["running", "stopped"]) — Whether the monitor has been initialized
  - `time`: string (required) — ISO 8601 UTC timestamp of the health check

## GET /llms-full.txt

Full agent endpoint reference

Integration guide plus a reference rendered from the current OpenAPI schema.

Authentication: Not required.

### Parameters

None.

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `text/markdown`

- `body`: string (required)

#### 400: Bad request

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /llms.txt

Agent integration guide

Concise integration instructions, examples, and callback contracts for agents.

Authentication: Not required.

### Parameters

None.

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `text/markdown`

- `body`: string (required)

#### 400: Bad request

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `text/markdown`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /payments

List payments

Lists all payments for the caller, newest first.

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: array (required)
  - `items`: object (optional) — Current payment status and observed transaction confirmations.
    - `payment_id`: string (required) — UUID identifying the payment
    - `status`: string (required) — PENDING, PAID, or EXPIRED
    - `chain`: string (required) — Chain identifier
    - `address`: string (required) — Receive wallet address
    - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
    - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
    - `tx_hash`: any (required) — Transaction hash once detected
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)
    - `confirmations`: integer (required) — Number of block confirmations

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## POST /payments

Create payment

Create a monitored payment intent. Allocates a receive address from the caller's pool,
converts the USD amount to an exact native-token amount, and begins monitoring the chain.
The callback URL is POSTed with transaction details once the transfer is confirmed.

Authentication: Required (X-API-Key header).

### Parameters

- `header x-api-key`: string (required)

### Request body

Media type: `application/json`

- `body`: object (required) — Create an exact-amount payment and its confirmation callback.
  - `usd_amount`: any (required) — Payment amount in USD
    - `anyOf 1`: number (optional)
    - `anyOf 2`: string (optional)
  - `chain`: string (required) — Target chain: bitcoin, ethereum, base, arbitrum, optimism, polygon, bnb, avalanche, linea, zksync
  - `callback_url`: string (required) — URL to receive webhook on confirmation

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Allocated payment address, exact amount, and expiry.
  - `payment_id`: string (required) — UUID identifying the payment
  - `chain`: string (required) — Chain identifier
  - `address`: string (required) — Allocated receive address
  - `amount_wei`: string (required) — Exact amount in smallest unit
  - `amount_native`: string (required) — Amount in native token
  - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
  - `expires_at`: string (required) — ISO 8601 expiry timestamp

#### 400: Unsupported chain or no addresses registered for that chain

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: No active subscription

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Monthly event limit reached

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Failed to fetch price or reach chain RPC

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /payments/{payment_id}

Get payment status

Returns the current status of a single payment owned by the caller.

Authentication: Required (X-API-Key header).

### Parameters

- `path payment_id`: string (required)
- `header x-api-key`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Current payment status and observed transaction confirmations.
  - `payment_id`: string (required) — UUID identifying the payment
  - `status`: string (required) — PENDING, PAID, or EXPIRED
  - `chain`: string (required) — Chain identifier
  - `address`: string (required) — Receive wallet address
  - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
  - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
  - `tx_hash`: any (required) — Transaction hash once detected
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `confirmations`: integer (required) — Number of block confirmations

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Invalid API key

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Payment not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /plans

List plans

Returns all available subscription plans with their event limits and pricing.

Authentication: Not required.

### Parameters

None.

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: array (required)
  - `items`: object (optional) — A subscription plan and its monthly allowance and price.
    - `name`: string (required) — Plan name
    - `monthly_event_limit`: integer (required) — Maximum payment events per month
    - `monthly_price_usd`: integer (required) — Monthly subscription price in USD

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## POST /signup

Create account

Sign up via EFF donation or paid plan subscription.

**method=donation**: Returns an API key immediately for a pending account. Donate the exact amount to the EFF address to activate payments, and poll the intent for account status.
**method=subscribe**: Subscribes to a paid plan (growth/scale) immediately. Returns API key.

Authentication: Not required.

### Parameters

None.

### Request body

Media type: `application/json`

- `body`: object (required) — Choose EFF donation signup or immediate paid-plan signup.
  - `method`: string (required, enum=["donation", "subscribe"]) — Signup method: donation or subscribe
  - `chain`: any (optional) — Required for donation method: bitcoin, ethereum, or binance
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `usd_amount`: any (optional) — Donation amount in USD; required for donation method
    - `anyOf 1`: number (optional)
    - `anyOf 2`: string (optional)
    - `anyOf 3`: null (optional)
  - `plan`: string (optional, default="starter") — Plan name: starter, growth, or scale
  - `email`: any (optional) — Optional contact email
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: any (required)
  - `anyOf 1`: object (optional) — Immediate account credentials and the donation needed to activate payments.
    - `api_key`: string (required) — 64-char hex API key. Store securely — cannot be recovered.
    - `user_id`: string (required) — UUID of the new user
    - `plan`: string (required) — Subscribed plan name
    - `account_status`: string (required) — Account awaits donation confirmation
    - `donation`: object (required) — Exact EFF donation instructions that activate this account
      - `intent_id`: string (required) — UUID identifying the signup intent
      - `chain`: string (required) — Chain identifier
      - `address`: string (required) — Receive wallet address
      - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
      - `amount_smallest`: string (required) — Exact amount in smallest units (wei or satoshis), as a string
      - `usd_amount`: string (required) — Requested amount in USD, as a decimal string
      - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp
  - `anyOf 2`: object (optional) — New active account credentials returned by subscription signup.
    - `user_id`: string (required) — UUID of the new user
    - `api_key`: string (required) — 64-char hex API key. Store securely — cannot be recovered.
    - `plan`: string (required) — Subscribed plan name
    - `account_status`: string (required) — Account is ready to create payments

#### 400: Invalid parameters

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Failed to fetch price

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /signup/options

Get signup options

Returns available donation chains and plan tiers for signup.

Authentication: Not required.

### Parameters

None.

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Available donation chains and signup plans.
  - `min_donation_usd`: integer (required) — Minimum signup donation in USD
  - `chains`: array (required) — Supported signup donation chains
    - `items`: object (optional) — An available EFF donation chain and its receive address.
      - `chain`: string (required) — Chain identifier
      - `address`: string (required) — Receive wallet address
      - `symbol`: string (required) — Native token symbol
      - `auto_verify`: boolean (required) — Whether on-chain donations can be verified automatically
  - `plans`: array (required) — Available signup plans
    - `items`: object (optional) — A signup plan with event allowance, price, and minimum donation.
      - `name`: string (required) — Plan name
      - `monthly_event_limit`: integer (required) — Maximum payment events per month
      - `monthly_price_usd`: integer (required) — Monthly subscription price in USD
      - `min_donation_usd`: integer (required) — Minimum signup donation in USD

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

## GET /signup/{intent_id}

Poll donation status

Returns the current status of a signup intent.

Authentication: Not required.

### Parameters

- `path intent_id`: string (required)

### Request body

None.

### Responses

#### 200: Successful Response

Media type: `application/json`

- `body`: object (required) — Current signup donation status and transaction information.
  - `account_status`: string (required, enum=["pending", "active"]) — Linked account activation status; pending if no subscription exists
  - `intent_id`: string (required) — UUID identifying the signup intent
  - `status`: string (required) — PENDING, CONFIRMED, or EXPIRED
  - `chain`: string (required) — Chain identifier
  - `amount_native`: string (required) — Exact amount in the native token, as a decimal string
  - `tx_hash`: any (required) — Transaction hash once detected, otherwise null
    - `anyOf 1`: string (optional)
    - `anyOf 2`: null (optional)
  - `expires_at`: string (required) — ISO 8601 UTC expiry timestamp

#### 400: Bad request

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 401: Unauthorized

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 403: Forbidden

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 404: Intent not found

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 409: Conflict

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 422: Request validation failed

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 429: Too many requests

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 502: Upstream error

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)

#### 503: Service unavailable

Media type: `application/json`

- `body`: object (required) — Consistent envelope for HTTP and request-validation errors.
  - `error`: object (required) — Error code, message, and optional hint
    - `code`: string (required) — Machine-readable error code
    - `message`: string (required) — Human-readable explanation
    - `hint`: any (optional) — Optional guidance; validation errors contain the full error list as JSON text
      - `anyOf 1`: string (optional)
      - `anyOf 2`: null (optional)
