# API Reference

> SatsRail REST API documentation. Non-custodial Bitcoin payment infrastructure, on-chain or Lightning.

Source: https://www.satsrail.com/developers/

Generated from the OpenAPI 3.0.3 spec: https://www.satsrail.com/developers/data/satsrail-openapi.json

Base URL: `https://app.satsrail.com`

## Authentication

Send the key as `Authorization: Bearer <key>`.

- `merchant_api_key` (bearer) — Merchant secret API key: `sk_live_* / sk_test_*`
- `public_embed_key` (bearer) — Publishable embed key for client-side integrations: `pk_live_*`

## Errors

Errors share one shape. This is a `401`:

```json
{
  "error": {
    "code": "missing_token",
    "message": "Missing API token. Include 'Authorization: Bearer YOUR_API_KEY' header.",
    "status": 401,
    "request_id": "16801347-037f-4ccb-ae22-78100ecde612"
  }
}
```

## Endpoints

Merchant API:

- `POST /api/v1/m/access/verify` — verify
- `GET /api/v1/m/api_tokens/{id}/usage` — Get API token usage
- `GET /api/v1/m/catalog` — show
- `POST /api/v1/m/invoices/generate` — generate
- `GET /api/v1/m/invoices/{id}` — show
- `GET /api/v1/m/invoices/{id}/qr` — qr
- `GET /api/v1/m/invoices/{id}/status` — status
- `GET /api/v1/m/media_keys` — index
- `POST /api/v1/m/media_keys` — create
- `GET /api/v1/m/media_keys/{id}` — show
- `PATCH /api/v1/m/media_keys/{id}` — update
- `DELETE /api/v1/m/media_keys/{id}` — destroy
- `POST /api/v1/m/media_keys/{id}/clear_old_key` — clear_old_key
- `GET /api/v1/m/media_keys/{id}/key` — key
- `POST /api/v1/m/media_keys/{id}/products` — link_products
- `DELETE /api/v1/m/media_keys/{id}/products/{product_id}` — unlink_product
- `POST /api/v1/m/media_keys/{id}/rotate` — rotate
- `GET /api/v1/m/merchant` — show
- `GET /api/v1/m/orders` — index
- `POST /api/v1/m/orders` — create
- `GET /api/v1/m/orders/{id}` — show
- `PATCH /api/v1/m/orders/{id}` — update
- `DELETE /api/v1/m/orders/{id}` — destroy
- `POST /api/v1/m/payment_requests` — create
- `GET /api/v1/m/payment_requests/{id}` — show
- `GET /api/v1/m/payment_requests/{id}/status` — status
- `GET /api/v1/m/payments` — index
- `GET /api/v1/m/payments/{id}` — show
- `GET /api/v1/m/products` — List products
- `POST /api/v1/m/products` — Create a product
- `GET /api/v1/m/products/{id}` — Get a product
- `PATCH /api/v1/m/products/{id}` — Update a product
- `DELETE /api/v1/m/products/{id}` — Archive a product
- `POST /api/v1/m/products/{product_id}/media_keys` — link_to_product
- `DELETE /api/v1/m/products/{product_id}/media_keys/{id}` — unlink_from_product
- `GET /api/v1/m/sales` — index
- `GET /api/v1/m/sales/{id}` — show
- `POST /api/v1/m/sessions` — create
- `POST /api/v1/m/sessions/activate` — activate
- `GET /api/v1/m/wallets` — index
- `GET /api/v1/m/wallets/{id}` — show
- `GET /api/v1/m/webhooks` — index
- `POST /api/v1/m/webhooks` — create
- `GET /api/v1/m/webhooks/{id}` — show
- `PATCH /api/v1/m/webhooks/{id}` — update
- `DELETE /api/v1/m/webhooks/{id}` — destroy

Public API:

- `POST /api/v1/pub/access/verify` — verify
- `GET /api/v1/pub/categories` — index
- `GET /api/v1/pub/channels` — index
- `POST /api/v1/pub/checkout_sessions` — create
- `GET /api/v1/pub/creators` — index
- `GET /api/v1/pub/exchanges` — index
- `GET /api/v1/pub/subscription_plans` — index

## Merchant API

### POST /api/v1/m/access/verify

Access: verify

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Request body (`application/json`):

- `access_token` (string, required)
- `media_key_id` (string)

```json
{
  "access_token": "eyJfcmFpbHMiOnsiZGF0YSI6eyJvcmRlcl9pZCI6IjIxNGQ1YmU4LTA1ZDktNGJiNC04MjRmLWE0NmJhZTg0NDY0YyIsInByb2R1Y3RfaWQiOiI1ZmU3NjIzZi1mMzVkLTQ1MzktOWViZC1lNjUxMGUwZjMxMTUiLCJtZXJjaGFudF9pZCI6Ijk0Y2EzNDJhLTA2ODAtNGM0NS1iZjI5LTc1ZDU2MzRhMDBjMyIsImV4cCI6MTc5MDcwNzg3N30sImV4cCI6IjIwMjYtMDktMjlUMTg6NTE6MTcuOTc5WiIsInB1ciI6ImFjY2Vzc190b2tlbiJ9fQ--26622505cab02bb993c21b1af075be31c8ca75ff77574d77543a8bc6c0b6fb06",
  "media_key_id": "86471e94-02b3-4596-8b5a-fefddab8460a"
}
```

Responses:

- `200` releases a linked media key when media_key_id is named (without it, no key is returned)
- `402` refuses media keys to a v1 token, which names no product

Example `200` response:

```json
{
  "valid": true,
  "remaining_seconds": 86399,
  "server_time": 1790621477,
  "expires_at": 1790707876,
  "product_id": "5fe7623f-f35d-4539-9ebd-e6510e0f3115",
  "order_id": "214d5be8-05d9-4bb4-824f-a46bae84464c",
  "media_key_id": "86471e94-02b3-4596-8b5a-fefddab8460a",
  "key": "EXAMPLEonly-media-key-base64url-32-bytes",
  "key_fingerprint": "db5a28ed53da2b953bdc39f08b362317ca358042a6cc5bb4d507a59bbbc3e136"
}
```

### GET /api/v1/m/api_tokens/{id}/usage

Api token: Get API token usage

Returns usage statistics and rate limit configuration for a specific API token. Only active tokens owned by the authenticated merchant are accessible.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Path parameters:

- `id` (string, required): API token UUID

Responses:

- `200` returns token usage data
- `404` token not found or not owned by merchant
- `401` unauthorized

Example `200` response:

```json
{
  "id": "string",
  "name": "string",
  "token_type": "string",
  "rpm_limit": "integer",
  "monthly_request_count": "integer",
  "last_request_counted_at": "string",
  "last_used_at": "string",
  "created_at": "string"
}
```

### GET /api/v1/m/catalog

Catalog: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer pk_live_YOUR_PUBLISHABLE_KEY`

Responses:

- `200` allows access with publishable key
- `401` requires authentication

Example `200` response:

```json
{
  "object": "catalog",
  "merchant_id": "2657842b-5006-4e6e-8488-3158e7c4806e",
  "catalog_version": "2026-03-14T03:11:55Z",
  "catalog_refresh_minutes": 30,
  "product_types": [
    {
      "id": "49708280-8d66-4cd6-9378-8e256e337160",
      "name": "Default",
      "position": 0,
      "products": [],
      "tax_ids": []
    }
  ],
  "taxes": [],
  "discounts": [],
  "livemode": true
}
```

### POST /api/v1/m/invoices/generate

Invoice: generate

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Request body (`application/json`):

- `order_id` (string, required)

```json
{
  "order_id": "cf1e996d-c303-46ea-b95e-6a7315689e70"
}
```

Responses:

- `201` returns the existing invoice
- `401` requires authentication
- `422` amount_exceeds_wallet_limits: the Lightning amount, in sats after price conversion and the conversion fee, is more than every one of the merchant's Lightning wallets takes in one payment (each wallet's max_payment_sats, set on the merchant's Wallets page).
- `404` returns not found error

Example `201` response:

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "3117b19c-4fff-4eaf-b0e4-b7406fce5d85",
  "payment_method": "lightning",
  "amount_sats": 10000,
  "status": "pending",
  "expires_at": "2026-03-14T04:11:55.467Z",
  "paid_at": null,
  "order_id": "cf1e996d-c303-46ea-b95e-6a7315689e70",
  "created_at": "2026-03-14T03:11:55.467Z",
  "updated_at": "2026-03-14T03:11:55.467Z",
  "payment_request": "lnbc10n1pj9q...",
  "payment_hash": "2eee05aec53912f0a2836d0ff78a2d934c43a2cdd02825d9e8c6fad2be86c81f",
  "expired": false,
  "time_until_expiry": 3599,
  "metadata": {
    "provider": "lnurl_demo",
    "mode": "mock"
  },
  "order": {
    "object": "order",
    "livemode": true,
    "id": "cf1e996d-c303-46ea-b95e-6a7315689e70",
    "order_number": "ORD-1773457915-4",
    "total_amount_cents": 10000,
    "tax_amount_cents": 0,
    "discount_amount_cents": 0,
    "total_amount_sats": 100000,
    "status": "pending",
    "merchant_id": "ef16bf22-8597-49af-b4df-a00ada990124",
    "channel_id": null,
    "wallet_id": "c85c322b-878c-4e67-a264-2abf023d335f",
    "discount_id": null,
    "created_at": "2026-03-14T03:11:55.464Z",
    "updated_at": "2026-03-14T03:11:55.464Z",
    "tender_type": "bitcoin",
    "items": [
      {
        "name": "Item 1",
        "price_cents": 10000,
        "qty": 1
      }
    ],
    "po_number": "ORD-1773457915-4",
    "btc_usd_price_at_creation": "100000.0",
    "btc_usd_rate_source": "coinbase",
    "currency": "usd",
    "btc_fiat_price_at_creation": "100000.0",
    "metadata": {}
  }
}
```

### GET /api/v1/m/invoices/{id}

Invoice: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `857d59e6-fe7d-4929-a3d5-3b5d0e92e8d7`

Responses:

- `200` includes order with correct JSON structure
- `401` requires authentication
- `404` prevents access to other merchant's invoices

Example `200` response:

```json
{
  "object": "invoice",
  "livemode": true,
  "id": "371bb5ac-a9a3-4251-83e7-5c5e1b9584ee",
  "payment_method": "lightning",
  "amount_sats": 10000,
  "status": "pending",
  "expires_at": "2026-03-14T04:11:55.505Z",
  "paid_at": null,
  "order_id": "70f52843-3b70-42d8-9953-111f0e840773",
  "created_at": "2026-03-14T03:11:55.505Z",
  "updated_at": "2026-03-14T03:11:55.505Z",
  "payment_request": "lnbc30n1pj9q...",
  "payment_hash": "b0df78ea711a2db64e6eb6c552ffe00b58800e8d8406bc791e7f0afcfcacee73",
  "expired": false,
  "time_until_expiry": 3599,
  "metadata": {},
  "order": {
    "object": "order",
    "livemode": true,
    "id": "70f52843-3b70-42d8-9953-111f0e840773",
    "order_number": "ORD-1773457915-6",
    "total_amount_cents": 10000,
    "tax_amount_cents": 0,
    "discount_amount_cents": 0,
    "total_amount_sats": 100000,
    "status": "pending",
    "merchant_id": "39b83ee2-3906-45c3-b7c9-d055bea10e44",
    "channel_id": null,
    "wallet_id": "c1832e80-72b1-4ae3-a49e-22e1bfffbaef",
    "discount_id": null,
    "created_at": "2026-03-14T03:11:55.502Z",
    "updated_at": "2026-03-14T03:11:55.502Z",
    "tender_type": "bitcoin",
    "items": [
      {
        "name": "Item 1",
        "price_cents": 10000,
        "qty": 1
      }
    ],
    "po_number": "ORD-1773457915-6",
    "btc_usd_price_at_creation": "100000.0",
    "btc_usd_rate_source": "coinbase",
    "currency": "usd",
    "btc_fiat_price_at_creation": "100000.0",
    "metadata": {}
  }
}
```

### GET /api/v1/m/invoices/{id}/qr

Invoice: qr

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `9f024127-e229-49a6-998c-057f832241a6`

Responses:

- `200` generates QR code with payment_request
- `401` requires authentication
- `404` prevents access to other merchant's invoice QR

A `200` response is `image/svg+xml`.

### GET /api/v1/m/invoices/{id}/status

Invoice: status

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `4b37eb1a-c52a-49e2-a327-c9d355c8d4c4`

Responses:

- `200` returns expired status
- `401` requires authentication
- `404` prevents checking status of other merchant's invoice

Example `200` response:

```json
{
  "status": "expired",
  "paid": false,
  "expired": true,
  "paid_at": null,
  "expires_at": "2026-03-14T02:11:55.758Z",
  "payment_method": "lightning",
  "payment": {
    "object": "payment",
    "livemode": true,
    "id": "fd1eba72-cc81-4267-8173-e129c6ced838",
    "amount_sats": 10000,
    "preimage": "preimage_0788fb6dc7e7b0554af6f0c90fc12b79_1",
    "confirmed_at": "2026-03-14T03:11:55.735Z",
    "order_id": "9f2a1e84-7748-41ac-aec9-44e74d44d928",
    "invoice_id": "7f0aecaf-d021-479f-a568-ff3a43ce40a0",
    "created_at": "2026-03-14T03:11:55.735Z",
    "updated_at": "2026-03-14T03:11:55.735Z",
    "channel_id": null,
    "payment_hash": "e838979f44ef5bc6e69005352881862ee56c0f4849a07062b91e5b46d93b47c1",
    "settled_at": "2026-03-14T03:11:55.735Z",
    "btc_usd_price_at_payment": "100000.0",
    "usd_value_at_payment": "10.0",
    "usd_variance_cents": -9000,
    "variance_percentage": "-90.0",
    "currency": "usd",
    "btc_fiat_price_at_payment": "100000.0",
    "fiat_value_at_payment": "10.0",
    "fiat_variance_cents": -9000,
    "confirmations": 0,
    "required_confirmations": 1,
    "metadata": {},
    "order": {
      "object": "order",
      "livemode": true,
      "id": "9f2a1e84-7748-41ac-aec9-44e74d44d928",
      "order_number": "ORD-1773457915-18",
      "total_amount_cents": 10000,
      "tax_amount_cents": 0,
      "discount_amount_cents": 0,
      "total_amount_sats": 100000,
      "status": "paid",
      "merchant_id": "cd2a76bd-53f5-4461-8129-660f18f955dc",
      "channel_id": null,
      "wallet_id": "2d425f9c-7817-4818-ab9a-26547c2b1405",
      "discount_id": null,
      "created_at": "2026-03-14T03:11:55.728Z",
      "updated_at": "2026-03-14T03:11:55.738Z",
      "tender_type": "bitcoin",
      "items": [
        {
          "name": "Item 1",
          "price_cents": 10000,
          "qty": 1
        }
      ],
      "po_number": "ORD-1773457915-18",
      "btc_usd_price_at_creation": "100000.0",
      "btc_usd_rate_source": "coinbase",
      "currency": "usd",
      "btc_fiat_price_at_creation": "100000.0",
      "metadata": {}
    }
  }
}
```

### GET /api/v1/m/media_keys

Media key: index

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Responses:

- `200` lists media keys
- `403` refuses keys that bridge an OAuth-connected assistant

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "object": "media_key",
      "livemode": true,
      "id": "9849fec0-44ae-40ac-99ba-293bb1ee7cff",
      "merchant_id": "95d63ac6-1625-4105-9dad-dafe5b12a9ce",
      "name": "Video key",
      "key_fingerprint": "2736be3e2cff047837483b34d47efe4ff8811da318b56856fab2a5be8c048beb",
      "rotation_pending": false,
      "browser_delivery": false,
      "product_ids": [
        "01109d09-a655-457a-ad78-37d62166ee9d"
      ],
      "metadata": {},
      "created_at": "2026-09-28T21:18:58.454Z",
      "updated_at": "2026-09-28T21:18:58.454Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  }
}
```

### POST /api/v1/m/media_keys

Media key: create

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Request body (`application/json`):

- `media_key` (object)
- `media_key.name` (string)
- `media_key.browser_delivery` (boolean)
- `media_key.product_ids` (string[])

```json
{
  "media_key": {
    "name": "Video key",
    "browser_delivery": true,
    "product_ids": [
      "6f9d703d-09ac-4625-8cd3-3ec856f74fd2"
    ]
  }
}
```

Responses:

- `201` links products by id or slug in the same request
- `404` creates nothing when a product is unknown or belongs to another merchant

Example `201` response (lists cut to their first item):

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "9e5e7fe3-e975-416f-b695-56c16241cbd3",
  "merchant_id": "96c3e316-923e-4e8a-a86b-b16ce0aafa55",
  "name": null,
  "key_fingerprint": "aa6453240caa89e1083273f6273e314c919e7fbbae567418e904c3739efe9793",
  "rotation_pending": false,
  "browser_delivery": false,
  "product_ids": [
    "2fa6f3b5-a44d-47f4-a41f-1eb520471f0a"
  ],
  "metadata": {},
  "created_at": "2026-09-28T21:18:52.050Z",
  "updated_at": "2026-09-28T21:18:52.050Z"
}
```

### GET /api/v1/m/media_keys/{id}

Media key: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `not-a-uuid`

Responses:

- `200` lists and shows the merchant's keys with their products
- `404` hides other merchants' keys

Example `200` response (lists cut to their first item):

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "24da3441-b528-4024-a644-92bb5128e8b5",
  "merchant_id": "1266078b-ab5d-46b8-bf3c-24b15bdbbe5d",
  "name": "Video key",
  "key_fingerprint": "f1e9efc16cb7e202cfd05203c19925ddae2cad821dfb8c03bc941370b755591e",
  "rotation_pending": false,
  "browser_delivery": false,
  "product_ids": [
    "8c0b7625-0818-4920-a5df-c8895d606352"
  ],
  "metadata": {},
  "created_at": "2026-09-28T21:18:53.166Z",
  "updated_at": "2026-09-28T21:18:53.166Z"
}
```

### PATCH /api/v1/m/media_keys/{id}

Media key: update

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `6f517730-8e25-40b2-b024-723419718568`

Request body (`application/json`):

- `media_key` (object, required)
- `media_key.browser_delivery` (boolean, required)
- `media_key.key` (string)

```json
{
  "media_key": {
    "browser_delivery": true,
    "key": "attacker-key"
  }
}
```

Responses:

- `200` updates a media key
- `400` hides other merchants' keys and needs a body
- `403` requires manage access for key custody and links

Example `200` response:

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "b1d7b020-17eb-48a4-8f83-1f221e2348d7",
  "merchant_id": "fb5c4efa-4d5b-488b-8f18-05e51b0029ab",
  "name": "Video key",
  "key_fingerprint": "a481a639027046fd752f8c52a1c96fce1cadde7be781ca18bc41ace53022f8e5",
  "rotation_pending": false,
  "browser_delivery": true,
  "product_ids": [],
  "metadata": {},
  "created_at": "2026-09-28T21:18:58.174Z",
  "updated_at": "2026-09-28T21:18:58.209Z"
}
```

### DELETE /api/v1/m/media_keys/{id}

Media key: destroy

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `5fe33e01-dcf1-485a-8a54-275baeb68180`

Responses:

- `204` destroys a key and its links

### POST /api/v1/m/media_keys/{id}/clear_old_key

Media key: clear_old_key

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `c0d8d917-e602-49cb-b93a-6ee7a3a601f6`

Request body (`application/json`):

```json
{}
```

Responses:

- `200` rotates, keeps the previous key until cleared, and notifies webhooks

Example `200` response:

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "c0d8d917-e602-49cb-b93a-6ee7a3a601f6",
  "merchant_id": "bc850edb-56fb-48db-aece-bb73ad182886",
  "name": "Video key",
  "key_fingerprint": "4d04688ea3f056330e55f2714870a4be618fa6f55ef782d318118ee504da93b7",
  "rotation_pending": false,
  "browser_delivery": false,
  "product_ids": [],
  "metadata": {},
  "created_at": "2026-09-28T21:18:54.607Z",
  "updated_at": "2026-09-28T21:18:54.683Z"
}
```

### GET /api/v1/m/media_keys/{id}/key

Media key: key

Header parameters:

- `Authorization` (string, required), e.g. `Bearer pk_live_YOUR_PUBLISHABLE_KEY`

Path parameters:

- `id` (string, required), e.g. `1d266228-5d2b-484e-9369-db7f749cd313`

Responses:

- `200` returns key material only from the key endpoint
- `403` refuses publishable keys

Example `200` response:

```json
{
  "object": "media_key_secret",
  "id": "46db8bcc-10ac-47fa-98c4-d84e2a6a4aa1",
  "key": "EXAMPLEonly-media-key-base64url-32-bytes",
  "key_fingerprint": "db5a28ed53da2b953bdc39f08b362317ca358042a6cc5bb4d507a59bbbc3e136",
  "previous_key": null,
  "previous_key_fingerprint": null
}
```

### POST /api/v1/m/media_keys/{id}/products

Media key: link_products

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `45ad786b-530f-424a-9ccd-3f965fd9f11f`

Request body (`application/json`):

- `product_ids` (string[], required)

```json
{
  "product_ids": [
    "c21a8a3b-12b3-414f-b9ec-5672875d187c"
  ]
}
```

Responses:

- `200` links products to a media key
- `400` requires a bounded list

Example `200` response (lists cut to their first item):

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "45ad786b-530f-424a-9ccd-3f965fd9f11f",
  "merchant_id": "93492e20-9ec7-4a07-a85f-f190b8a34dc6",
  "name": "Video key",
  "key_fingerprint": "bfcd0c3f43088f9ac2a69832fda5be910f5025efde7ba145dc7a78dafee4d217",
  "rotation_pending": false,
  "browser_delivery": false,
  "product_ids": [
    "c21a8a3b-12b3-414f-b9ec-5672875d187c"
  ],
  "metadata": {},
  "created_at": "2026-09-28T21:18:59.049Z",
  "updated_at": "2026-09-28T21:18:59.049Z"
}
```

### DELETE /api/v1/m/media_keys/{id}/products/{product_id}

Media key: unlink_product

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `e06f85b8-9462-429c-b046-34b1d912f932`
- `product_id` (string, required), e.g. `589a3798-7e6d-49ff-b96d-5c6e932a7fa2`

Responses:

- `200` links from the key side and unlinks one product

Example `200` response:

```json
{
  "object": "media_key",
  "livemode": true,
  "id": "e06f85b8-9462-429c-b046-34b1d912f932",
  "merchant_id": "2dc5660c-ee8d-4199-8062-0c36c2aa7253",
  "name": "Video key",
  "key_fingerprint": "554779381f4352fca9c28017544444c9938c988074365054679366126288dd96",
  "rotation_pending": false,
  "browser_delivery": false,
  "product_ids": [
    "265b3db6-8157-4e0d-b4f4-71020b616b74"
  ],
  "metadata": {},
  "created_at": "2026-09-28T21:18:55.184Z",
  "updated_at": "2026-09-28T21:18:55.184Z"
}
```

### POST /api/v1/m/media_keys/{id}/rotate

Media key: rotate

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `a28a806e-1927-43ad-a316-b3cd70e8c00f`

Request body (`application/json`):

```json
{}
```

Responses:

- `200` rotates a media key

Example `200` response:

```json
{
  "object": "media_key_secret",
  "id": "a28a806e-1927-43ad-a316-b3cd70e8c00f",
  "key": "EXAMPLEonly-media-key-base64url-32-bytes",
  "key_fingerprint": "db5a28ed53da2b953bdc39f08b362317ca358042a6cc5bb4d507a59bbbc3e136",
  "previous_key": "EXAMPLEonly-previous-key-base64url-32-bytes",
  "previous_key_fingerprint": "8bc62288bb5e4910fbbd3560fbb75dcd09bc9fb9e39bf29c2dc8ab16c1603ee8"
}
```

### GET /api/v1/m/merchant

Merchant: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Responses:

- `200` allows API access
- `401` returns 401 for GET /api/v1/merchant (token revoked on terminate)
- `403` returns 403 forbidden for GET /api/v1/merchant

Example `200` response:

```json
{
  "object": "merchant",
  "id": "030f701e-19ec-429d-93b0-ce36a444c5da",
  "name": "Merchant 64",
  "email": "merchant64@example.com",
  "phone": "(555) 555-0100",
  "business_license": "LICENSE-001",
  "status": "active",
  "time_zone": "UTC",
  "currency": "usd",
  "created_at": "2026-03-14T03:11:56.312Z",
  "updated_at": "2026-03-14T03:11:56.312Z",
  "tip_options": [],
  "logo_url": null,
  "pos_theme": "dark",
  "pos_enabled": true,
  "privapaid_enabled": false,
  "channels": [],
  "wallets": [
    {
      "object": "wallet",
      "id": "d91382af-e191-4117-9c57-7e8a657800a6",
      "name": "Wallet 65",
      "status": "active",
      "enabled": true,
      "position": 67,
      "created_at": "2026-03-14T03:11:56.316Z",
      "updated_at": "2026-03-14T03:11:56.316Z",
      "operational": true,
      "lightning": {
        "lightning_address": "testmerchant70@phoenix.acinq.co",
        "max_payment_sats": null
      }
    }
  ],
  "taxes": [],
  "livemode": true
}
```

### GET /api/v1/m/orders

Order: index

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_YOUR_SECRET_KEY`

Query parameters:

- `per_page` (integer), e.g. `10`
- `q[created_at_gteq]` (string), e.g. `2026-03-07T03:11:56Z`
- `q[created_at_lteq]` (string), e.g. `2026-03-14T03:11:56Z`
- `q[merchant_id_eq]` (string), e.g. `724f689e-1e73-4462-947a-d8ff6e05b89b`
- `q[payments_payment_type_eq]` (integer), e.g. `1`
- `q[status_eq]` (string), e.g. `paid`

Responses:

- `200` test token only sees test orders
- `401` rejects requests without Bearer prefix
- `403` returns 403 forbidden for GET /api/v1/orders

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "object": "order",
      "livemode": false,
      "id": "5ae83d27-4639-416f-b06f-8550a9c9aa90",
      "order_number": "ORD-1773457918-114",
      "total_amount_cents": 10000,
      "tax_amount_cents": 0,
      "discount_amount_cents": 0,
      "total_amount_sats": 100000,
      "status": "pending",
      "merchant_id": "8e2a625e-d5c0-4654-b655-38ce54cb1f01",
      "channel_id": null,
      "wallet_id": "8c854cc8-d7d0-49eb-9c09-593bf3452d3f",
      "discount_id": null,
      "created_at": "2026-03-14T03:11:58.346Z",
      "updated_at": "2026-03-14T03:11:58.346Z",
      "tender_type": "bitcoin",
      "items": [
        {
          "name": "Item 1",
          "price_cents": 10000,
          "qty": 1
        }
      ],
      "po_number": "ORD-1773457918-114",
      "btc_usd_price_at_creation": "100000.0",
      "btc_usd_rate_source": "coinbase",
      "currency": "usd",
      "btc_fiat_price_at_creation": "100000.0",
      "metadata": {}
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  }
}
```

### POST /api/v1/m/orders

Order: create

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_YOUR_SECRET_KEY`

Request body (`application/json`):

- `order` (object, required)
- `order.total_amount_cents` (integer, required)
- `order.customer_email` (string, email)
- `order.items` (object[], required)
- `order.items[].name` (string, required)
- `order.items[].price_cents` (integer, required)
- `order.items[].qty` (integer, required)
- `order.tender_type` (string)
- `order.currency` (string)
- `generate_invoice` (boolean)
- `mark_as_paid` (boolean)

```json
{
  "order": {
    "total_amount_cents": 5000,
    "items": [
      {
        "name": "Widget",
        "price_cents": 5000,
        "qty": 1
      }
    ],
    "tender_type": "cash",
    "currency": "usd"
  },
  "generate_invoice": true,
  "mark_as_paid": true
}
```

Responses:

- `201` creates orders with correct livemode flag from test token
- `401` requires authentication
- `422` returns validation errors with correct structure; or amount_exceeds_wallet_limits: the Lightning amount, in sats after price conversion and the conversion fee, is more than every one of the merchant's Lightning wallets takes in one payment (each wallet's max_payment_sats, set on the merchant's Wallets page).

Example `201` response:

```json
{
  "object": "order",
  "livemode": false,
  "id": "e6e9ba68-bde9-4938-8585-6d3df8aa5c08",
  "order_number": "ORD-6AE0989B55CA9F17",
  "total_amount_cents": 5000,
  "tax_amount_cents": 0,
  "discount_amount_cents": 0,
  "total_amount_sats": 50000,
  "status": "pending",
  "merchant_id": "1ba083e9-2701-43d5-b375-cceb65ecb130",
  "channel_id": null,
  "wallet_id": "42ae58e1-896a-4476-b286-a44ad10f8986",
  "discount_id": null,
  "created_at": "2026-03-14T03:11:58.382Z",
  "updated_at": "2026-03-14T03:11:58.382Z",
  "tender_type": "bitcoin",
  "items": [
    {
      "name": "Widget",
      "price_cents": 5000,
      "qty": 1
    }
  ],
  "po_number": "ORD-6AE0989B55CA9F17",
  "btc_usd_price_at_creation": "100000.0",
  "btc_usd_rate_source": "coinbase",
  "currency": "usd",
  "btc_fiat_price_at_creation": "100000.0",
  "metadata": {},
  "invoice": {
    "object": "invoice",
    "livemode": true,
    "id": "fff4313b-f93e-4321-baa2-6fcfa17fbb18",
    "payment_method": "lightning",
    "amount_sats": 10000,
    "status": "pending",
    "expires_at": "2026-03-14T03:26:56.679Z",
    "paid_at": null,
    "order_id": "a2158e12-b22d-42b7-8217-ee90f3b4ff01",
    "created_at": "2026-03-14T03:11:56.679Z",
    "updated_at": "2026-03-14T03:11:56.679Z",
    "payment_request": "lnbc10000u1ped5914a06083b199c797e26f359383d28c9679ba5ad59f2b73cb85a69691eee4",
    "payment_hash": "f911c798e78ad250c5b093e3f0838245d6bf0a4004371cd6c44a68caf63c4c18",
    "expired": false,
    "time_until_expiry": 899,
    "metadata": {
      "mode": "mock",
      "provider": "lnurl_demo"
    }
  },
  "cash_paid_at": "2026-03-14T03:11:57.031Z"
}
```

### GET /api/v1/m/orders/{id}

Order: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `bcb3cddc-2fc3-456a-8e93-ae9dceb3d50b`

Responses:

- `200` includes livemode field in order responses
- `401` requires authentication
- `404` live token cannot access test order by ID

Example `200` response:

```json
{
  "object": "order",
  "livemode": true,
  "id": "bcb3cddc-2fc3-456a-8e93-ae9dceb3d50b",
  "order_number": "ORD-1773457918-117",
  "total_amount_cents": 10000,
  "tax_amount_cents": 0,
  "discount_amount_cents": 0,
  "total_amount_sats": 100000,
  "status": "pending",
  "merchant_id": "b9475619-be78-4b05-b040-fa85171dab1a",
  "channel_id": null,
  "wallet_id": "a405197e-6f13-42d8-bd90-901db95a8efe",
  "discount_id": null,
  "created_at": "2026-03-14T03:11:58.466Z",
  "updated_at": "2026-03-14T03:11:58.466Z",
  "tender_type": "bitcoin",
  "items": [
    {
      "name": "Item 1",
      "price_cents": 10000,
      "qty": 1
    }
  ],
  "po_number": "ORD-1773457918-117",
  "btc_usd_price_at_creation": "100000.0",
  "btc_usd_rate_source": "coinbase",
  "currency": "usd",
  "btc_fiat_price_at_creation": "100000.0",
  "metadata": {},
  "merchant": {
    "object": "merchant",
    "id": "b9475619-be78-4b05-b040-fa85171dab1a",
    "name": "Merchant 176",
    "email": "merchant176@example.com",
    "phone": "(555) 555-0100",
    "business_license": "LICENSE-001",
    "status": "active",
    "time_zone": "UTC",
    "currency": "usd",
    "created_at": "2026-03-14T03:11:58.460Z",
    "updated_at": "2026-03-14T03:11:58.460Z",
    "tip_options": [],
    "logo_url": null,
    "pos_theme": "dark",
    "pos_enabled": true,
    "privapaid_enabled": false,
    "channels": [],
    "wallets": [
      {
        "object": "wallet",
        "id": "a405197e-6f13-42d8-bd90-901db95a8efe",
        "name": "Wallet 177",
        "status": "active",
        "enabled": true,
        "position": 179,
        "created_at": "2026-03-14T03:11:58.464Z",
        "updated_at": "2026-03-14T03:11:58.464Z",
        "operational": true,
        "lightning": {
          "lightning_address": "testmerchant182@phoenix.acinq.co",
          "max_payment_sats": null
        }
      }
    ],
    "taxes": []
  },
  "cash_paid_at": "2026-03-14T03:11:57.078Z",
  "payment": {
    "object": "payment",
    "livemode": true,
    "id": "4fcd5112-5dc7-428b-a58a-b4c94c861721",
    "amount_sats": null,
    "preimage": null,
    "confirmed_at": "2026-03-14T03:11:57.078Z",
    "order_id": "62037323-3c46-46b8-a04d-5320808d2326",
    "invoice_id": null,
    "created_at": "2026-03-14T03:11:57.079Z",
    "updated_at": "2026-03-14T03:11:57.079Z",
    "channel_id": null,
    "payment_hash": null,
    "settled_at": "2026-03-14T03:11:57.078Z",
    "usd_value_at_payment": "100.0",
    "currency": "usd",
    "fiat_value_at_payment": "100.0",
    "confirmations": 0,
    "required_confirmations": 1,
    "metadata": {}
  }
}
```

### PATCH /api/v1/m/orders/{id}

Order: update

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `72aa9f7f-4751-46ad-b208-155d5e81ece3`

Request body (`application/json`):

- `order` (object, required)
- `order.total_amount_cents` (integer)
- `order.currency` (string)
- `order.customer_email` (string, email)
- `order.items` (object[])
- `order.items[].name` (string, required)
- `order.items[].price_cents` (integer, required)
- `order.items[].qty` (integer, required)
- `order.metadata` (object)
- `order.metadata.ref` (string)
- `order.metadata.note` (string)
- `order.status` (string)
- `order.wallet_id` (string)
- `order.tender_type` (string)

```json
{
  "order": {
    "total_amount_cents": 9999,
    "currency": "eur",
    "items": [
      {
        "name": "New Item",
        "price_cents": 2000,
        "qty": 1
      }
    ],
    "metadata": {
      "ref": "123",
      "note": "sneaky"
    },
    "status": "paid",
    "wallet_id": "e3099d67-a21a-47a9-ac82-dc6a0d88c168",
    "tender_type": "cash"
  }
}
```

Responses:

- `200` marks cash order as paid via status update
- `401` requires authentication
- `404` prevents updating other merchant's orders

Example `200` response:

```json
{
  "object": "order",
  "livemode": true,
  "id": "72aa9f7f-4751-46ad-b208-155d5e81ece3",
  "order_number": "ORD-1773457917-80",
  "total_amount_cents": 10000,
  "tax_amount_cents": 0,
  "discount_amount_cents": 0,
  "total_amount_sats": 100,
  "status": "paid",
  "merchant_id": "b30a692c-48f0-460f-940c-bfbca552fc17",
  "channel_id": null,
  "wallet_id": null,
  "discount_id": null,
  "created_at": "2026-03-14T03:11:57.057Z",
  "updated_at": "2026-03-14T03:11:57.064Z",
  "tender_type": "cash",
  "items": [
    {
      "name": "Item 1",
      "price_cents": 10000,
      "qty": 1
    }
  ],
  "po_number": "ORD-1773457917-80",
  "btc_usd_price_at_creation": "100000.0",
  "btc_usd_rate_source": "coinbase",
  "currency": "usd",
  "btc_fiat_price_at_creation": "100000.0",
  "metadata": {
    "ref": "123",
    "note": "sneaky"
  },
  "cash_paid_at": "2026-03-14T03:11:57.064Z",
  "merchant": {
    "object": "merchant",
    "id": "b30a692c-48f0-460f-940c-bfbca552fc17",
    "name": "Merchant 102",
    "email": "merchant102@example.com",
    "phone": "(555) 555-0100",
    "business_license": "LICENSE-001",
    "status": "active",
    "time_zone": "UTC",
    "currency": "usd",
    "created_at": "2026-03-14T03:11:57.052Z",
    "updated_at": "2026-03-14T03:11:57.052Z",
    "tip_options": [],
    "logo_url": null,
    "pos_theme": "dark",
    "pos_enabled": true,
    "privapaid_enabled": false,
    "channels": [],
    "wallets": [
      {
        "object": "wallet",
        "id": "a2507cb3-e5f7-40a4-abc7-a5efe52bb2c7",
        "name": "Wallet 104",
        "status": "active",
        "enabled": true,
        "position": 106,
        "created_at": "2026-03-14T03:11:57.056Z",
        "updated_at": "2026-03-14T03:11:57.056Z",
        "operational": true,
        "lightning": {
          "lightning_address": "testmerchant109@phoenix.acinq.co",
          "max_payment_sats": null
        }
      }
    ],
    "taxes": []
  },
  "payment": {
    "object": "payment",
    "livemode": true,
    "id": "8da30daa-e106-4496-bb1a-959d05a7c0b6",
    "amount_sats": null,
    "preimage": null,
    "confirmed_at": "2026-03-14T03:11:57.064Z",
    "order_id": "72aa9f7f-4751-46ad-b208-155d5e81ece3",
    "invoice_id": null,
    "created_at": "2026-03-14T03:11:57.065Z",
    "updated_at": "2026-03-14T03:11:57.065Z",
    "channel_id": null,
    "payment_hash": null,
    "settled_at": "2026-03-14T03:11:57.064Z",
    "usd_value_at_payment": "100.0",
    "currency": "usd",
    "fiat_value_at_payment": "100.0",
    "confirmations": 0,
    "required_confirmations": 1,
    "metadata": {}
  }
}
```

### DELETE /api/v1/m/orders/{id}

Order: destroy

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `68caf49a-1084-445f-9d68-4621a8ea8c3d`

Responses:

- `204` returns no content status
- `401` requires authentication
- `404` prevents cancelling other merchant's orders

### POST /api/v1/m/payment_requests

Payment request: create

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Request body (`application/json`):

- `payment_request` (object, required)
- `payment_request.amount_cents` (integer, required)
- `payment_request.payment_method` (string, required)
- `payment_request.description` (string, required)
- `payment_request.customer_email` (string, email)

```json
{
  "payment_request": {
    "amount_cents": 10000,
    "payment_method": "lightning",
    "description": "Test payment"
  }
}
```

Responses:

- `201` returns lightning-specific fields with correct types
- `401` requires authentication
- `422` returns error for invalid payment method; or amount_exceeds_wallet_limits: the Lightning amount, in sats after price conversion and the conversion fee, is more than every one of the merchant's Lightning wallets takes in one payment (each wallet's max_payment_sats, set on the merchant's Wallets page).

Example `201` response:

```json
{
  "object": "payment_request",
  "id": "1d36a2e1-aa00-4400-b24c-4ff451b47289",
  "payment_method": "lightning",
  "amount_sats": 10000,
  "created_at": "2026-03-14T03:11:57Z",
  "expires_at": "2026-03-14T04:11:57Z",
  "payment_request": "lnbc200n1pj9q...",
  "payment_hash": "7475af0f76afbd075f49698ea177c8b08f1cf39a15dee677064677756b3d1554"
}
```

### GET /api/v1/m/payment_requests/{id}

Payment request: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `e39ebc0b-1dbf-4386-9daa-3d6dbfb33ba9`

Responses:

- `200` returns paid status with paid_at
- `401` requires authentication
- `404` prevents access to other merchant's payment requests

Example `200` response:

```json
{
  "object": "payment_request",
  "id": "e39ebc0b-1dbf-4386-9daa-3d6dbfb33ba9",
  "payment_method": "lightning",
  "amount_sats": 10000,
  "created_at": "2026-03-14T03:11:57Z",
  "expires_at": "2026-03-14T04:11:57Z",
  "payment_request": "lnbc250n1pj9q...",
  "payment_hash": "95e9191c6bde0074eaa855d51372136cb327c1327b184aa7df85d48d9b8d64a7",
  "status": "paid",
  "paid_at": "2026-03-14T03:11:57Z",
  "amount_paid_sats": null
}
```

### GET /api/v1/m/payment_requests/{id}/status

Payment request: status

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `eeb10982-feea-478e-8bab-18a0daa27751`

Responses:

- `200` returns paid_at timestamp
- `401` requires authentication
- `404` prevents access to other merchant's payment requests

Example `200` response:

```json
{
  "id": "eeb10982-feea-478e-8bab-18a0daa27751",
  "status": "paid",
  "paid_at": "2026-03-14T03:11:57Z",
  "amount_sats": 10000,
  "amount_paid_sats": null
}
```

### GET /api/v1/m/payments

Payment: index

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Query parameters:

- `per_page` (integer), e.g. `10`
- `q[amount_sats_gteq]` (integer), e.g. `10000`
- `q[confirmed_at_gteq]` (string), e.g. `2026-03-07T03:11:57Z`
- `q[confirmed_at_lteq]` (string), e.g. `2026-03-14T03:11:57Z`
- `q[order_id_eq]` (string), e.g. `cf9959e1-fa40-46f6-9e1f-37e46eb628c7`

Responses:

- `200` only returns payments for the authenticated merchant
- `401` requires authentication

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "object": "payment",
      "livemode": true,
      "id": "02b006ee-4702-48fa-98f7-77b5de05058b",
      "amount_sats": 10000,
      "preimage": "preimage_9befbda3e23e242eac5fa0db8ab881ae_44",
      "confirmed_at": "2026-03-14T03:11:57.610Z",
      "order_id": "5959dde2-8bcd-43d0-bbb4-6994e6bfea8d",
      "invoice_id": "975788bb-d881-4927-8f13-5d9cfaf5a166",
      "created_at": "2026-03-14T03:11:57.611Z",
      "updated_at": "2026-03-14T03:11:57.611Z",
      "channel_id": null,
      "payment_hash": "b33ebd4143de8da227917204b4480f38e2c66a093f7d5d07b40d1a45f69d03b6",
      "settled_at": "2026-03-14T03:11:57.610Z",
      "btc_usd_price_at_payment": "100000.0",
      "usd_value_at_payment": "10.0",
      "usd_variance_cents": -9000,
      "variance_percentage": "-90.0",
      "currency": "usd",
      "btc_fiat_price_at_payment": "100000.0",
      "fiat_value_at_payment": "10.0",
      "fiat_variance_cents": -9000,
      "confirmations": 0,
      "required_confirmations": 1,
      "metadata": {},
      "order": {
        "object": "order",
        "livemode": true,
        "id": "5959dde2-8bcd-43d0-bbb4-6994e6bfea8d",
        "order_number": "ORD-1773457917-104",
        "total_amount_cents": 10000,
        "tax_amount_cents": 0,
        "discount_amount_cents": 0,
        "total_amount_sats": 100000,
        "status": "paid",
        "merchant_id": "80161a5f-b712-4072-890b-bad1dc3f9b0a",
        "channel_id": null,
        "wallet_id": "2ce37d06-aec3-408a-8d99-619a37cef996",
        "discount_id": null,
        "created_at": "2026-03-14T03:11:57.607Z",
        "updated_at": "2026-03-14T03:11:57.607Z",
        "tender_type": "bitcoin",
        "items": [
          {
            "name": "Item 1",
            "price_cents": 10000,
            "qty": 1
          }
        ],
        "po_number": "ORD-1773457917-104",
        "btc_usd_price_at_creation": "100000.0",
        "btc_usd_rate_source": "coinbase",
        "currency": "usd",
        "btc_fiat_price_at_creation": "100000.0",
        "metadata": {}
      },
      "invoice": {
        "object": "invoice",
        "livemode": true,
        "id": "975788bb-d881-4927-8f13-5d9cfaf5a166",
        "payment_method": "lightning",
        "amount_sats": 10000,
        "status": "paid",
        "expires_at": "2026-03-14T04:11:57.609Z",
        "paid_at": null,
        "order_id": "5959dde2-8bcd-43d0-bbb4-6994e6bfea8d",
        "created_at": "2026-03-14T03:11:57.609Z",
        "updated_at": "2026-03-14T03:11:57.609Z",
        "payment_request": "lnbc380n1pj9q...",
        "payment_hash": "b33ebd4143de8da227917204b4480f38e2c66a093f7d5d07b40d1a45f69d03b6",
        "expired": false,
        "time_until_expiry": 3599,
        "metadata": {}
      }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  }
}
```

### GET /api/v1/m/payments/{id}

Payment: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `c1ecee92-955b-4fa0-894b-20fa391c15aa`

Responses:

- `200` includes timestamp information with correct types
- `401` requires authentication
- `404` prevents access to other merchant's payments

Example `200` response:

```json
{
  "object": "payment",
  "livemode": true,
  "id": "c1ecee92-955b-4fa0-894b-20fa391c15aa",
  "amount_sats": 10000,
  "preimage": "preimage_89a7358f79c0c62abef0b1b18d504354_50",
  "confirmed_at": "2026-03-14T03:11:57.740Z",
  "order_id": "46c49a03-b055-4d86-b406-15a2c612051f",
  "invoice_id": "752fe94f-25e6-4b4a-9323-9801bda1c9b2",
  "created_at": "2026-03-14T03:11:57.741Z",
  "updated_at": "2026-03-14T03:11:57.741Z",
  "channel_id": null,
  "payment_hash": "5a63a65dcbb2d6d87015a34a6163e4864b39657d6843abbc878b4aec54b00ac6",
  "settled_at": "2026-03-14T03:11:57.740Z",
  "btc_usd_price_at_payment": "100000.0",
  "usd_value_at_payment": "10.0",
  "usd_variance_cents": -9000,
  "variance_percentage": "-90.0",
  "currency": "usd",
  "btc_fiat_price_at_payment": "100000.0",
  "fiat_value_at_payment": "10.0",
  "fiat_variance_cents": -9000,
  "confirmations": 0,
  "required_confirmations": 1,
  "metadata": {},
  "order": {
    "object": "order",
    "livemode": true,
    "id": "46c49a03-b055-4d86-b406-15a2c612051f",
    "order_number": "ORD-1773457917-110",
    "total_amount_cents": 10000,
    "tax_amount_cents": 0,
    "discount_amount_cents": 0,
    "total_amount_sats": 100000,
    "status": "paid",
    "merchant_id": "f441a972-a1cd-47b9-8738-8ce101ba00f2",
    "channel_id": null,
    "wallet_id": "9d9411f0-0fd4-4a9c-af6d-b07390826a11",
    "discount_id": null,
    "created_at": "2026-03-14T03:11:57.736Z",
    "updated_at": "2026-03-14T03:11:57.736Z",
    "tender_type": "bitcoin",
    "items": [
      {
        "name": "Item 1",
        "price_cents": 10000,
        "qty": 1
      }
    ],
    "po_number": "ORD-1773457917-110",
    "btc_usd_price_at_creation": "100000.0",
    "btc_usd_rate_source": "coinbase",
    "currency": "usd",
    "btc_fiat_price_at_creation": "100000.0",
    "metadata": {}
  },
  "invoice": {
    "object": "invoice",
    "livemode": true,
    "id": "752fe94f-25e6-4b4a-9323-9801bda1c9b2",
    "payment_method": "lightning",
    "amount_sats": 10000,
    "status": "paid",
    "expires_at": "2026-03-14T04:11:57.739Z",
    "paid_at": null,
    "order_id": "46c49a03-b055-4d86-b406-15a2c612051f",
    "created_at": "2026-03-14T03:11:57.739Z",
    "updated_at": "2026-03-14T03:11:57.739Z",
    "payment_request": "lnbc440n1pj9q...",
    "payment_hash": "5a63a65dcbb2d6d87015a34a6163e4864b39657d6843abbc878b4aec54b00ac6",
    "expired": false,
    "time_until_expiry": 3599,
    "metadata": {}
  }
}
```

### GET /api/v1/m/products

Product: List products

Returns a paginated list of products for the authenticated merchant. Supports filtering by name, status, sku, and date range.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Query parameters:

- `per_page` (integer), e.g. `25`
- `page` (integer), e.g. `1`
- `q[name_cont]` (string): Filter by name (contains)
- `q[status_eq]` (string): Filter by status, e.g. `active`
- `q[sku_eq]` (string): Filter by SKU
- `q[created_at_gteq]` (string): Created after (ISO 8601)
- `q[created_at_lteq]` (string): Created before (ISO 8601)

Responses:

- `200` returns paginated list of products
- `401` unauthorized — missing or invalid API token

Example `200` response:

```json
{
  "object": "string",
  "data": [
    {
      "object": "string",
      "livemode": "boolean",
      "id": "string",
      "name": "string",
      "description": "string",
      "slug": "string",
      "sku": "string",
      "price_cents": "integer",
      "currency": "string",
      "status": "string",
      "position": "integer",
      "resource_type": "string",
      "image_url": "string",
      "access_duration_seconds": "integer",
      "product_type_id": "string",
      "merchant_id": "string",
      "api_token_id": "string",
      "created_at": "string",
      "updated_at": "string",
      "formatted_price": "string",
      "payment_link_url": "string",
      "metadata": {}
    }
  ],
  "meta": {
    "current_page": "integer",
    "total_pages": "integer",
    "total_count": "integer",
    "per_page": "integer"
  }
}
```

### POST /api/v1/m/products

Product: Create a product

Creates a new product for the authenticated merchant. The product is automatically associated with the API token used to create it.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Request body (`application/json`):

- `product` (object, required)
- `product.name` (string, required)
- `product.description` (string)
- `product.price_cents` (integer, required)
- `product.sku` (string)
- `product.status` (string), one of `active`, `archived`
- `product.product_type_id` (string, uuid)
- `product.resource_type` (string)
- `product.image_url` (string)
- `product.access_duration_seconds` (integer)
- `product.metadata` (object)

```json
{
  "product": {
    "name": "Premium Content",
    "description": "Access to premium content",
    "price_cents": 5000,
    "sku": "PREM-001",
    "resource_type": "digital",
    "image_url": "https://example.com/image.png"
  }
}
```

Responses:

- `201` product created successfully
- `422` validation errors
- `401` unauthorized — missing or invalid API token

Example `201` response:

```json
{
  "object": "string",
  "livemode": "boolean",
  "id": "string",
  "name": "string",
  "description": "string",
  "slug": "string",
  "sku": "string",
  "price_cents": "integer",
  "currency": "string",
  "status": "string",
  "position": "integer",
  "resource_type": "string",
  "image_url": "string",
  "access_duration_seconds": "integer",
  "product_type_id": "string",
  "merchant_id": "string",
  "api_token_id": "string",
  "created_at": "string",
  "updated_at": "string",
  "formatted_price": "string",
  "payment_link_url": "string",
  "metadata": {}
}
```

### GET /api/v1/m/products/{id}

Product: Get a product

Returns a single product by slug.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Path parameters:

- `id` (string, required): Product slug, e.g. `premium-content`

Responses:

- `200` returns the product
- `404` product not found
- `401` unauthorized

Example `200` response:

```json
{
  "object": "string",
  "livemode": "boolean",
  "id": "string",
  "name": "string",
  "description": "string",
  "slug": "string",
  "sku": "string",
  "price_cents": "integer",
  "currency": "string",
  "status": "string",
  "position": "integer",
  "resource_type": "string",
  "image_url": "string",
  "access_duration_seconds": "integer",
  "product_type_id": "string",
  "merchant_id": "string",
  "api_token_id": "string",
  "created_at": "string",
  "updated_at": "string",
  "formatted_price": "string",
  "payment_link_url": "string",
  "metadata": {}
}
```

### PATCH /api/v1/m/products/{id}

Product: Update a product

Updates an existing product by slug.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Path parameters:

- `id` (string, required): Product slug, e.g. `premium-content`

Request body (`application/json`):

- `product` (object, required)
- `product.name` (string, required)
- `product.description` (string)
- `product.price_cents` (integer, required)
- `product.sku` (string)
- `product.status` (string), one of `active`, `archived`
- `product.product_type_id` (string, uuid)
- `product.resource_type` (string)
- `product.image_url` (string)
- `product.access_duration_seconds` (integer)
- `product.metadata` (object)

```json
{
  "product": {
    "name": "Premium Content",
    "description": "Access to premium content",
    "price_cents": 5000,
    "sku": "PREM-001",
    "resource_type": "digital",
    "image_url": "https://example.com/image.png"
  }
}
```

Responses:

- `200` product updated successfully
- `404` product not found
- `422` validation errors
- `401` unauthorized

Example `200` response:

```json
{
  "object": "string",
  "livemode": "boolean",
  "id": "string",
  "name": "string",
  "description": "string",
  "slug": "string",
  "sku": "string",
  "price_cents": "integer",
  "currency": "string",
  "status": "string",
  "position": "integer",
  "resource_type": "string",
  "image_url": "string",
  "access_duration_seconds": "integer",
  "product_type_id": "string",
  "merchant_id": "string",
  "api_token_id": "string",
  "created_at": "string",
  "updated_at": "string",
  "formatted_price": "string",
  "payment_link_url": "string",
  "metadata": {}
}
```

### DELETE /api/v1/m/products/{id}

Product: Archive a product

Soft-deletes (archives) a product by slug. The product is not permanently removed.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_test_...`

Path parameters:

- `id` (string, required): Product slug, e.g. `premium-content`

Responses:

- `204` product archived successfully
- `404` product not found
- `401` unauthorized

### POST /api/v1/m/products/{product_id}/media_keys

Media key: link_to_product

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `product_id` (string, required), e.g. `2109dc69-ea0c-4f00-8b8d-6f4430f539fd`

Request body (`application/json`):

- `media_key_ids` (string[], required)

```json
{
  "media_key_ids": [
    "a1d3271a-e83a-4885-80b6-cfd4ac4767b0"
  ]
}
```

Responses:

- `200` links media keys to a product
- `404` links nothing when any referenced key or product is not the merchant's

Example `200` response:

```json
{
  "object": "media_key_links",
  "product_id": "2109dc69-ea0c-4f00-8b8d-6f4430f539fd",
  "media_key_ids": [
    "a1d3271a-e83a-4885-80b6-cfd4ac4767b0"
  ]
}
```

### DELETE /api/v1/m/products/{product_id}/media_keys/{id}

Media key: unlink_from_product

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `067f1da0-3050-4863-9377-2e0036c5bfc4`
- `product_id` (string, required), e.g. `prod_72b688d24adb3768fc373240`

Responses:

- `200` links one product to many keys from the product side

Example `200` response:

```json
{
  "object": "media_key_links",
  "product_id": "42d59560-0bba-49f2-96c1-1caac37a60de",
  "media_key_ids": []
}
```

### GET /api/v1/m/sales

Sale: index

The sales ledger: one sale per paid order, newest sold_at first. A sale is written when its order is paid and kept after the order itself is deleted, so its id is the order's id. Read with a secret key whose security group reads orders; a test key lists test sales.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Query parameters:

- `page` (integer): Page number, from 1, e.g. `1`
- `per_page` (integer): Sales per page, up to 100 (default 25), e.g. `25`
- `q[status_eq]` (string): paid, shipped or refunded, e.g. `paid`
- `q[payment_method_eq]` (string): lightning, onchain, cash or card (a card payment on the merchant's own payment gateway), e.g. `lightning`
- `q[source_eq]` (string): Where it was sold
- `q[currency_eq]` (string): Currency code (usd, eur …), e.g. `usd`
- `q[order_number_eq]` (string): Exact order number
- `q[total_cents_gteq]` (integer): Minimum total_cents
- `q[total_cents_lteq]` (integer): Maximum total_cents
- `q[sold_at_gteq]` (string): Paid on or after (ISO 8601), e.g. `2026-10-01T00:00:00Z`
- `q[sold_at_lteq]` (string): Paid on or before (ISO 8601), e.g. `2026-10-31T23:59:59Z`
- `q[ordered_at_gteq]` (string): Ordered on or after (ISO 8601)
- `q[ordered_at_lteq]` (string): Ordered on or before (ISO 8601)

Responses:

- `200` One page of sales
- `401` No key, or a key that is not valid
- `403` A publishable key, a key whose security group cannot read orders, or a merchant that is not operational

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "object": "sale",
      "livemode": true,
      "id": "75bf8f43-9db0-4c19-8999-49fec22e4820",
      "order_id": "75bf8f43-9db0-4c19-8999-49fec22e4820",
      "order_number": "ORD-1792367765-10",
      "merchant_id": "c90b2c08-c36b-498c-add4-f40c6b483860",
      "status": "paid",
      "source": "api",
      "currency": "usd",
      "ordered_at": "2026-10-18T23:56:05.095Z",
      "sold_at": "2026-10-18T23:56:05.111Z",
      "subtotal_cents": 10000,
      "discount_cents": 0,
      "discount_id": null,
      "discount_name": null,
      "tax_cents": 0,
      "tax_details": [],
      "conversion_fee_percent": "0.0",
      "conversion_fee_cents": 0,
      "total_cents": 10000,
      "amount_due_cents": 10000,
      "refunded_cents": 0,
      "payment_method": "lightning",
      "payment_id": "8df2a5d4-bb6e-4135-8a8f-f62818663d80",
      "amount_sats": 123456,
      "btc_price_at_order": "100000.0",
      "btc_price_at_payment": "100000.0",
      "btc_usd_price_at_payment": "100000.0",
      "fiat_value_at_payment": "123.46",
      "usd_value_at_payment": "123.46",
      "fiat_variance_cents": 2346,
      "usd_variance_cents": 2346,
      "variance_percentage": "23.46",
      "payment_hash": "047df6e686854c9bf0a3a20a7e096e66cdc89bdf69719df60ecbedb159f459e5",
      "preimage": "preimage_09e46fd744a1ea1dcb9d5f11d7b66ff0_5",
      "wallet_id": "5d499b9d-5928-4922-b220-f6f4dd08bc00",
      "wallet_name": "Wallet 5",
      "api_token_id": null,
      "customer_name": null,
      "customer_email": "ada@example.com",
      "delivery_status": null,
      "shipped_at": null,
      "delivered_at": null,
      "margin_percent": null,
      "margin_amount_sats": null,
      "settlement_amount_sats": null,
      "metadata": {},
      "receipt_url": "http://www.example.com/receipt/75bf8f43-9db0-4c19-8999-49fec22e4820",
      "items": [
        {
          "id": "4920ac07-c9f2-4a4a-a012-b3e8c2be8d20",
          "name": "Beans",
          "qty": 2,
          "price_cents": 5000,
          "line_total_cents": 10000,
          "tax_rate": null,
          "tax_amount_cents": 0,
          "product_id": null,
          "product_sku": null,
          "product_type_id": null,
          "product_type_name": null,
          "total_amount_sats": null,
          "margin_amount_sats": null,
          "settlement_amount_sats": null
        }
      ],
      "created_at": "2026-10-18T23:56:05.133Z",
      "updated_at": "2026-10-18T23:56:05.133Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  }
}
```

### GET /api/v1/m/sales/{id}

Sale: show

A sale by its id, which is its order's id. It answers the same after the order has been deleted. Another merchant's sale, or one of the other mode, is not found.

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required): The sale's id (the order's id), e.g. `4f1c2d4e-8a3b-4c5d-9e7f-0a1b2c3d4e5f`

Responses:

- `200` The sale
- `403` A key whose security group cannot read orders
- `404` No such sale for this merchant and mode

Example `200` response:

```json
{
  "object": "sale",
  "livemode": true,
  "id": "dce6f343-be30-47a2-ac66-0d6530cf018c",
  "order_id": "dce6f343-be30-47a2-ac66-0d6530cf018c",
  "order_number": "ORD-1792367767-22",
  "merchant_id": "ba0ece0e-2d00-4f7c-b578-bdc08e147188",
  "status": "paid",
  "source": "api",
  "currency": "usd",
  "ordered_at": "2026-10-18T23:56:07.056Z",
  "sold_at": "2026-10-18T23:56:07.070Z",
  "subtotal_cents": 10000,
  "discount_cents": 0,
  "discount_id": null,
  "discount_name": null,
  "tax_cents": 0,
  "tax_details": [],
  "conversion_fee_percent": "0.0",
  "conversion_fee_cents": 0,
  "total_cents": 10000,
  "amount_due_cents": 10000,
  "refunded_cents": 0,
  "payment_method": "lightning",
  "payment_id": "01d06b40-dbd6-4367-82f1-87fed3c97b14",
  "amount_sats": 123456,
  "btc_price_at_order": "100000.0",
  "btc_price_at_payment": "100000.0",
  "btc_usd_price_at_payment": "100000.0",
  "fiat_value_at_payment": "123.46",
  "usd_value_at_payment": "123.46",
  "fiat_variance_cents": 2346,
  "usd_variance_cents": 2346,
  "variance_percentage": "23.46",
  "payment_hash": "f95b44ae4dae5a7169e38d0d7e0d9fc27ea522cba6de967c6c7f1a58783ec89b",
  "preimage": "preimage_c8f38b7a622999af9d1ea7e71d9cd897_11",
  "wallet_id": "8b147ad8-87e8-4746-9489-afecb3daf820",
  "wallet_name": "Wallet 13",
  "api_token_id": null,
  "customer_name": null,
  "customer_email": "ada@example.com",
  "delivery_status": null,
  "shipped_at": null,
  "delivered_at": null,
  "margin_percent": null,
  "margin_amount_sats": null,
  "settlement_amount_sats": null,
  "metadata": {},
  "receipt_url": "http://www.example.com/receipt/dce6f343-be30-47a2-ac66-0d6530cf018c",
  "items": [
    {
      "id": "dd1fef20-1b0b-48f7-abaa-fc848b6f52d9",
      "name": "Beans",
      "qty": 2,
      "price_cents": 5000,
      "line_total_cents": 10000,
      "tax_rate": null,
      "tax_amount_cents": 0,
      "product_id": null,
      "product_sku": null,
      "product_type_id": null,
      "product_type_name": null,
      "total_amount_sats": null,
      "margin_amount_sats": null,
      "settlement_amount_sats": null
    }
  ],
  "created_at": "2026-10-18T23:56:07.090Z",
  "updated_at": "2026-10-18T23:56:07.090Z"
}
```

### POST /api/v1/m/sessions

Session: create

Request body (`application/json`):

- `email` (string, required)
- `password` (string, required)

```json
{
  "email": "user28@example.com",
  "password": "password123456"
}
```

Responses:

- `200` resets failed attempts on successful login
- `400` returns 400 for missing credentials
- `401` increments failed attempts on wrong password
- `423` rejects valid credentials when account is locked

Example `200` response:

```json
{
  "session_token": "eyJfcmFpbHMiOnsiZGF0YSI6eyJ1c2VyX2lkIjoiYjdjNDA4NGYtOWYxYi00MjRmLWE4ODMtZjQ2NWU1ZmRhZTE0IiwiZXhwIjoxNzczNDU5NzE4fSwicHVyIjoicG9zX3Nlc3Npb24ifX0=--dfc82ff6369ff136daace5c67630f30259ad9f92",
  "merchants": [
    {
      "id": "c0326624-c893-4701-a99e-bea7fa0b67ca",
      "name": "Merchant 153",
      "logo_url": null,
      "currency": "usd",
      "role": "owner"
    }
  ]
}
```

### POST /api/v1/m/sessions/activate

Session: activate

Request body (`application/json`):

- `session_token` (string, required)
- `merchant_id` (string, required)

```json
{
  "session_token": "eyJfcmFpbHMiOnsiZGF0YSI6eyJ1c2VyX2lkIjoiOTZjZDY3MzEtZGJkZi00MWEwLWEwNGYtNWQ1MjgzZTk0ZTU5IiwiZXhwIjoxNzczNDU5NzE4fSwicHVyIjoicG9zX3Nlc3Npb24ifX0=--981626a7484c548066b74e2ecd5b83b6cd664e53",
  "merchant_id": "c378f7fe-c4ab-47b4-9660-48865734bcfb"
}
```

Responses:

- `200` still allows activation (POS available to all plans)
- `400` returns 400 for missing params
- `401` returns 401 for invalid session token
- `403` returns 403 for non-operational merchant
- `404` returns 404 for merchant with no active API key

Example `200` response:

```json
{
  "api_key": "sk_live_YOUR_SECRET_KEY",
  "merchant": {
    "id": "c378f7fe-c4ab-47b4-9660-48865734bcfb",
    "name": "Merchant 165",
    "currency": "usd"
  }
}
```

### GET /api/v1/m/wallets

Wallet: index

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Query parameters:

- `q[enabled_eq]` (string), e.g. `true`
- `q[merchant_id_eq]` (string), e.g. `d81b5fe2-2df5-4709-ab99-b8a7e0c2b322`

Responses:

- `200` includes lightning section with lightning_address
- `401` requires authentication

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "object": "wallet",
      "id": "0251fcd0-5751-4a9c-b3f1-101117555196",
      "name": "Wallet 189",
      "status": "active",
      "enabled": true,
      "position": 191,
      "created_at": "2026-03-14T03:11:58.590Z",
      "updated_at": "2026-03-14T03:11:58.590Z",
      "operational": true,
      "lightning": {
        "lightning_address": "shop@phoenix.acinq.co",
        "max_payment_sats": null
      }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  }
}
```

### GET /api/v1/m/wallets/{id}

Wallet: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `21a10ea0-c9fe-406f-a02e-713ac4ef43d8`

Responses:

- `200` does not include display_name, status_color, or next_payment_address
- `401` requires authentication
- `404` returns 404 with correct error structure for non-existent wallet

Example `200` response:

```json
{
  "object": "wallet",
  "id": "b93639c1-8515-4935-adf7-020415a44411",
  "name": "Wallet 193",
  "status": "active",
  "enabled": true,
  "position": 195,
  "created_at": "2026-03-14T03:11:58.637Z",
  "updated_at": "2026-03-14T03:11:58.637Z",
  "operational": true,
  "lightning": {
    "lightning_address": "testmerchant198@phoenix.acinq.co",
    "max_payment_sats": null
  },
  "last_checked_at": null,
  "last_transaction_at": null,
  "last_error": null,
  "consecutive_failures": 0
}
```

### GET /api/v1/m/webhooks

Webhook: index

Lists your webhook endpoints and every event one can subscribe to (available_events). Customer subscriptions (a Pro feature) send subscription.* events, each delivered once with the usual signature and retries: subscription.created (you created one and its invitation went out), subscription.activated (the customer accepted it), subscription.trial_will_end (a few days before a trial ends), subscription.renewed (a billing period was paid), subscription.past_due (a period is still unpaid after the past-due days), subscription.paused, subscription.resumed (once each time) and subscription.cancelled. data is the subscription: id, status (invited, trialing, active, past_due, paused, cancelled), customer_id, customer_email, product_id, name, amount_cents, first_charge_cents, currency, interval_unit (week, month, year), interval_count, starts_on, trial_days, trial_ends_at, bitcoin_discount_percent, rail (bitcoin, card), next_billing_at, current_period_start, current_period_end, invited_at, accepted_at, paused_at, cancelled_at, created_at. renewed and past_due add period: the billing period's order (id, order_number, status, total_amount_cents, items, billing_period_start, billing_period_end). Each period is also an ordinary order, so order.* and payment.* events fire for it as for any order.

Header parameters:

- `Authorization` (string, required), e.g. `sk_live_YOUR_SECRET_KEY`

Query parameters:

- `q[active_eq]` (string), e.g. `true`
- `q[merchant_id_eq]` (string), e.g. `40cba8fd-87fc-45db-bb45-2de7fdceab8e`

Responses:

- `200` only returns webhooks for the authenticated merchant
- `401` rejects requests without Bearer prefix

Example `200` response (lists cut to their first item):

```json
{
  "object": "list",
  "data": [
    {
      "object": "webhook",
      "livemode": true,
      "id": "f0bdc2b8-8f71-4dcd-a4ad-dc81929e651f",
      "url": "https://example.com/webhooks",
      "description": "Test Webhook",
      "events": [
        "order.created"
      ],
      "active": true,
      "success_count": 0,
      "failure_count": 0,
      "last_triggered_at": null,
      "created_at": "2026-03-14T03:11:58.971Z",
      "updated_at": "2026-03-14T03:11:58.971Z",
      "health_percentage": 100,
      "metadata": {}
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 1,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null
  },
  "available_events": [
    "order.created"
  ]
}
```

### POST /api/v1/m/webhooks

Webhook: create

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Request body (`application/json`):

- `webhook` (object, required)
- `webhook.url` (string, required)
- `webhook.events` (string[], required)
- `webhook.description` (string)

```json
{
  "webhook": {
    "url": "https://example.com/hook",
    "events": [
      "order.created"
    ],
    "description": "My test webhook"
  }
}
```

Responses:

- `201` creates a webhook with all available event types
- `401` requires authentication
- `422` returns validation errors for invalid url format

Example `201` response (lists cut to their first item):

```json
{
  "object": "webhook",
  "livemode": true,
  "id": "db705776-1673-4757-8732-fa46cd868d1c",
  "url": "https://example.com/hook",
  "description": null,
  "events": [
    "order.created"
  ],
  "active": true,
  "success_count": 0,
  "failure_count": 0,
  "last_triggered_at": null,
  "created_at": "2026-03-14T03:11:59.075Z",
  "updated_at": "2026-03-14T03:11:59.075Z",
  "health_percentage": 100,
  "metadata": {},
  "secret_key": "whsec_YOUR_WEBHOOK_SECRET"
}
```

### GET /api/v1/m/webhooks/{id}

Webhook: show

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `e3ae1c97-c3eb-48bc-9fcd-5d48065884db`

Responses:

- `200` does not expose secret_key on show
- `401` requires authentication
- `404` prevents access to other merchant's webhooks

Example `200` response (lists cut to their first item):

```json
{
  "object": "webhook",
  "livemode": true,
  "id": "982ebe1a-819e-4d95-981e-3c77eca16d23",
  "url": "https://example.com/webhooks",
  "description": "Test Webhook",
  "events": [
    "order.created"
  ],
  "active": true,
  "success_count": 0,
  "failure_count": 0,
  "last_triggered_at": null,
  "created_at": "2026-03-14T03:11:58.995Z",
  "updated_at": "2026-03-14T03:11:58.995Z",
  "health_percentage": 100,
  "metadata": {}
}
```

### PATCH /api/v1/m/webhooks/{id}

Webhook: update

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `07aed364-1bd1-4311-8872-a9694627b335`

Request body (`application/json`):

- `webhook` (object, required)
- `webhook.url` (string)
- `webhook.events` (string[])
- `webhook.active` (boolean)
- `webhook.description` (string)

```json
{
  "webhook": {
    "url": "https://updated.example.com/hook",
    "events": [
      "payment.confirmed"
    ],
    "active": false,
    "description": "Updated"
  }
}
```

Responses:

- `200` does not expose secret_key on update
- `401` requires authentication
- `404` prevents updating other merchant's webhooks
- `422` returns validation errors for invalid events

Example `200` response (lists cut to their first item):

```json
{
  "object": "webhook",
  "livemode": true,
  "id": "1ace2467-8c77-4f12-8139-54bc9fb206c8",
  "url": "https://example.com/webhooks",
  "description": "Updated",
  "events": [
    "order.created"
  ],
  "active": true,
  "success_count": 0,
  "failure_count": 0,
  "last_triggered_at": null,
  "created_at": "2026-03-14T03:11:59.197Z",
  "updated_at": "2026-03-14T03:11:59.202Z",
  "health_percentage": 100,
  "metadata": {}
}
```

### DELETE /api/v1/m/webhooks/{id}

Webhook: destroy

Header parameters:

- `Authorization` (string, required), e.g. `Bearer sk_live_YOUR_SECRET_KEY`

Path parameters:

- `id` (string, required), e.g. `e885ab37-4295-41e4-bebc-3287142a555f`

Responses:

- `204` returns no content status
- `401` requires authentication
- `404` prevents deleting other merchant's webhooks

## Public API

### POST /api/v1/pub/access/verify

Access: verify

Header parameters:

- `Authorization` (string, required), e.g. `Bearer pk_live_YOUR_PUBLISHABLE_KEY`

Request body (`application/json`):

- `access_token` (string)

```json
{
  "access_token": "eyJfcmFpbHMiOnsiZGF0YSI6eyJwYXltZW50X2lkIjoicGF5LXRlc3QtdXVpZCIsInJlc291cmNlX2lkIjoicHJvZF9hYmMiLCJyZXNvdXJjZV90eXBlIjoic3RyZWFtIiwiZXhwIjoxNzczNDU4NTE5fSwiZXhwIjoiMjAyNi0wMy0xNFQwMzoyMTo1OS4zMDdaIiwicHVyIjoiYWNjZXNzX3Rva2VuIn19--1c6d12e1f86a6bba3be28f425669b2a0adf2449b"
}
```

Responses:

- `200` accepts token via X-Access-Token header
- `400` returns 400 when access_token param is missing
- `401` returns 401 without a publishable key
- `402` returns 402 for a tampered token
- `403` returns 403 when merchant has privapaid disabled

Example `200` response:

```json
{
  "valid": true,
  "remaining_seconds": 600,
  "resource_id": "prod_abc",
  "resource_type": "stream"
}
```

### GET /api/v1/pub/categories

Category: index

Responses:

- `200` sets public cache headers

Example `200` response (lists cut to their first item):

```json
{
  "object": "list",
  "data": [
    "Film & Animation"
  ]
}
```

### GET /api/v1/pub/channels

Channel: index

Query parameters:

- `category` (string), e.g. `Gaming`
- `nsfw` (string), e.g. `true`
- `page` (integer), e.g. `1`
- `per_page` (integer), e.g. `2`
- `q` (string), e.g. `Gaming`

Responses:

- `200` defaults to 24 per page

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "slug": "chan-1",
      "name": "Channel 15",
      "bio": "A test channel for streaming.",
      "videos_count": 0,
      "nsfw": false,
      "theme": "rose",
      "category": "Entertainment",
      "profile_image_url": null,
      "social_links": {},
      "merchant_name": "Merchant 250",
      "country": "IQ",
      "country_name": "Country 224",
      "language": "en",
      "currency": "usd"
    }
  ],
  "meta": {
    "current_page": 1,
    "total_pages": 1,
    "total_count": 1,
    "per_page": 24
  }
}
```

### POST /api/v1/pub/checkout_sessions

Checkout session: create

Header parameters:

- `Authorization` (string, required), e.g. `Bearer pk_live_YOUR_PUBLISHABLE_KEY`

Request body (`application/json`):

- `checkout_session` (object, required)
- `checkout_session.amount_cents` (integer)
- `checkout_session.currency` (string)
- `checkout_session.product_id` (string)
- `checkout_session.customer_email` (string)
- `checkout_session.customer_name` (string)
- `checkout_session.metadata` (object)
- `checkout_session.metadata.order_ref` (string, required)
- `checkout_session.metadata.source` (string, required)

```json
{
  "checkout_session": {
    "amount_cents": 5000,
    "currency": "usd",
    "product_id": "prod_32bf92c24dbad85569bd9acd",
    "customer_email": "buyer@example.com",
    "customer_name": "Alice Smith",
    "metadata": {
      "order_ref": "MY-123",
      "source": "website"
    }
  }
}
```

Responses:

- `201` accepts metadata hash
- `401` requires authentication
- `422` returns validation error for invalid email

Example `201` response:

```json
{
  "object": "checkout_session",
  "livemode": true,
  "id": "1ba2644d-ca41-479d-9689-db2e0e0b4657",
  "token": "cs_Ku60a_FLU38Nls2UOZyfvdjIs39RzcItqAiV0NK9IsQ",
  "checkout_url": "http://www.example.com/checkout/cs_Ku60a_FLU38Nls2UOZyfvdjIs39RzcItqAiV0NK9IsQ",
  "expires_at": "2026-03-14T03:26:55Z",
  "amount_cents": 5000,
  "tax_amount_cents": 0,
  "tax_details": [],
  "currency": "usd",
  "status": "pending",
  "customer_email": null,
  "customer_name": null,
  "customer_phone": null,
  "customer_address": null,
  "checkout_mode": "iframe",
  "checkout_theme": "light",
  "checkout_accent_color": "#e8b53c",
  "privapaid_enabled": false,
  "metadata": {
    "order_ref": "MY-123",
    "source": "website"
  }
}
```

### GET /api/v1/pub/creators

Creator: index

Responses:

- `200` returns the same data as the channels endpoint

Example `200` response:

```json
{
  "object": "list",
  "data": [
    {
      "slug": "compat-test",
      "name": "Channel 16",
      "bio": "A test channel for streaming.",
      "videos_count": 0,
      "nsfw": false,
      "theme": "rose",
      "category": "Entertainment",
      "profile_image_url": null,
      "social_links": {},
      "merchant_name": "Merchant 251",
      "country": "IR",
      "country_name": "Country 225",
      "language": "en",
      "currency": "usd"
    }
  ],
  "meta": {
    "current_page": 1,
    "total_pages": 1,
    "total_count": 1,
    "per_page": 24
  }
}
```

### GET /api/v1/pub/exchanges

Exchange: index

Responses:

- `200` invalidates cache when an exchange_country is destroyed

Example `200` response (lists cut to their first item):

```json
{
  "exchanges": [
    {
      "id": "02f4199f-a5d2-4f93-ae93-581f4bafe650",
      "name": "Buda.com",
      "url": "https://buda.com",
      "promoted": true,
      "min_transaction_sats": null,
      "pays_lightning_invoices": null,
      "lightning_deposits": null,
      "payout_countries": [],
      "peer_to_peer": false,
      "available_worldwide": false,
      "source_url": "https://soporte.buda.com/es/articles/4690267-lightning-en-buda-com",
      "checked_on": "2026-10-15",
      "notes": null,
      "notes_es": null,
      "logo_url": null,
      "countries": [
        {
          "id": "5f355627-8c07-4c5e-a8a7-2f05fe7be640",
          "name": "Chile",
          "iso_code": "CL",
          "currency_code": "CLP"
        }
      ]
    }
  ],
  "updated_at": "2026-10-15T14:05:00.000Z"
}
```

### GET /api/v1/pub/subscription_plans

Subscription plan: index

Responses:

- `200` the plans on offer

Example `200` response (lists cut to their first item):

```json
{
  "plans": [
    {
      "slug": "free",
      "name": "Free",
      "description": "Bitcoin payments up to $500 a month — no credit card required",
      "price_cents": 0,
      "price_display": "Free",
      "max_monthly_volume_cents": 50000,
      "min_monthly_volume_cents": null,
      "features": [],
      "requires_kyc": false,
      "on_sale": true,
      "rate_limits": {
        "per_api_key_per_minute": 100,
        "per_api_key_per_hour": 1000,
        "delivery_per_order_per_hour": 30
      }
    }
  ]
}
```
