{
  "openapi": "3.1.0",
  "info": {
    "title": "relaybell API",
    "version": "1.2.0",
    "summary": "Web push notifications for any website.",
    "description": "The relaybell REST API: send web push notifications, track events, and manage segments, subscribers, webhooks and automations for a website. Base URL: `https://relaybell.com/api/v1`.\n\n**Authentication.** Send a secret API key (it starts with `rb_live_`) as `Authorization: Bearer rb_live_…`. Create keys in the dashboard under Settings → Developers → API keys; a key belongs to one website, so calls need no project id. Each key has scopes: an operation's `x-scope` names the one it needs (`full` allows everything; reads without a scope work with any key). Keep keys on your server, never in browser code. The routes the browser SDK calls (*Browser SDK*) take your public Project ID instead. A few routes work only from the signed-in dashboard; they say so.\n\n**Errors.** Every error is JSON `{ \"error\": \"code\", \"message\": \"…\", \"request_id\": \"…\" }` with a standard HTTP status (`message` is not always there). Branch on `error`, never on `message`. Every response carries the same id in `X-Request-Id` — quote it when you contact support. A body that isn't valid JSON answers `400 invalid_json`; bodies are limited to 1 MB (`413 payload_too_large`). Every error code: [relaybell.com/docs#errors](https://relaybell.com/docs#errors).\n\n**Rate limits.**\n\n| Requests | Limit | Counted per |\n| --- | --- | --- |\n| POST /notifications and /notifications/{id}/send (saving a draft isn't counted) | 60 a minute and 600 an hour | API key (dashboard: account) |\n| Broadcasts: sends to everyone, a segment, a topic or filters | 30 per clock hour | website |\n| Sends to specific users (externalId / externalIds), or transactional with a segment or filters | 1,000 per clock hour | website |\n| Test sends to your own devices | 120 per clock hour | website |\n| POST / GET /events with an API key | 3,000 a minute | API key |\n| Webhook resends and test events | 30 a minute | website |\n| Browser SDK routes (/projects/…, click and delivery pings) and /events without a key | 120 a minute | visitor IP |\n| POST /ai/suggest | 30 an hour | IP address |\n| POST /upload | 60 an hour | IP address |\n\nGoing over a request limit answers `429 rate_limited`; going over an hourly send budget answers `429 send_limit_reached` with `limit`, `kind` and `retryAfter`. Limited routes send `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`; every 429 sends `Retry-After` (seconds) — wait that long instead of retrying in a loop. Other API-key routes have no request limit of their own.\n\n**SDKs.** Server SDKs 1.2.0: npm `relaybell`, PyPI `relaybell`, Composer `relaybell/relaybell-php`. The full guide, with the browser SDK, merge tags and webhook signatures: [relaybell.com/docs](https://relaybell.com/docs).",
    "contact": {
      "name": "relaybell support",
      "url": "https://relaybell.com/contact"
    },
    "termsOfService": "https://relaybell.com/terms",
    "license": {
      "name": "Proprietary",
      "url": "https://relaybell.com/terms"
    }
  },
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://relaybell.com/docs"
  },
  "servers": [
    {
      "url": "https://relaybell.com/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Notifications",
      "description": "Send to everyone, a segment, a topic or one of your users; schedule, cancel, keep drafts, and read how each send did."
    },
    {
      "name": "Events",
      "description": "Behavioral events from your server: conversions, automation triggers and product alerts."
    },
    {
      "name": "Segments",
      "description": "Saved filters to send to (or leave out of a send), and live counts."
    },
    {
      "name": "Topics",
      "description": "Preference-center categories subscribers opt in to."
    },
    {
      "name": "Subscribers",
      "description": "Export, import, profiles, GDPR access and erasure, and the filterable subscriber list."
    },
    {
      "name": "Webhooks",
      "description": "Signed POSTs to your server when something happens, with a 30-day delivery log, resend and test events. See [Verify a webhook signature](https://relaybell.com/docs#webhook-verify)."
    },
    {
      "name": "Automations",
      "description": "One push per event: cart reminders, back-in-stock and price-drop alerts, welcome-back flows."
    },
    {
      "name": "Journeys",
      "description": "Multi-step journeys (drip sequences) that start on subscribe or on an event."
    },
    {
      "name": "RSS feeds",
      "description": "New posts from a feed, sent automatically."
    },
    {
      "name": "Recurring schedules",
      "description": "The same message every hour, day or week."
    },
    {
      "name": "Templates",
      "description": "Saved notification copy."
    },
    {
      "name": "Goals",
      "description": "Conversion goals and the funnel report."
    },
    {
      "name": "On-site messages",
      "description": "Banners and slide-ins shown on your pages."
    },
    {
      "name": "Stats and reports",
      "description": "Totals, daily numbers, growth and list health."
    },
    {
      "name": "Website settings",
      "description": "Sending rules, the opt-in prompt, the welcome push and the default logo."
    },
    {
      "name": "API keys",
      "description": "List and revoke keys (creating one needs the dashboard)."
    },
    {
      "name": "Identity verification",
      "description": "The secret your server signs user ids with, so nobody can claim another person's notifications."
    },
    {
      "name": "Uploads and AI",
      "description": "Image uploads and copy suggestions."
    },
    {
      "name": "Test devices",
      "description": "Your own browsers, for test sends that never reach subscribers."
    },
    {
      "name": "Browser SDK",
      "description": "Public routes the browser SDK and service worker call with your Project ID — no API key. 120 requests a minute per visitor IP."
    },
    {
      "name": "Integrations",
      "description": "Inbound webhooks from other platforms."
    }
  ],
  "paths": {
    "/notifications": {
      "post": {
        "operationId": "sendNotification",
        "tags": [
          "Notifications"
        ],
        "summary": "Send a notification",
        "description": "Accepts the send and answers 202; delivery happens in the background. With no targeting fields it goes to every subscriber of the website. Send to one of your own users with `externalId` (or `externalIds`), usually with `transactional: true`; see [Send to one user](https://relaybell.com/docs#send-user).\n\nWith `\"draft\": true` it saves a draft instead (201, never sent or counted against limits; a website keeps up to 500 drafts of up to 256 KB each).\n\nUntil the account's email address is verified it answers `403 email_not_verified`. Limits: 60 a minute and 600 an hour per API key (`429 rate_limited`), and per website 30 broadcasts and 1,000 sends to specific users per clock hour (`429 send_limit_reached`, with `Retry-After`).",
        "x-scope": "send",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              },
              "examples": {
                "broadcast": {
                  "summary": "To everyone",
                  "value": {
                    "title": "Flash sale 🔥",
                    "body": "30% off ends tonight — {name|there}!",
                    "url": "https://shop.example/sale"
                  }
                },
                "oneUser": {
                  "summary": "To one user (transactional)",
                  "value": {
                    "externalId": "user_123",
                    "transactional": true,
                    "title": "Your order has shipped",
                    "body": "Order #1042 is on its way.",
                    "url": "https://shop.example/orders/1042"
                  }
                },
                "segment": {
                  "summary": "A segment within a topic, scheduled",
                  "value": {
                    "title": "New arrivals",
                    "segmentId": "3f2b8c1e-6a4d-4e2f-9b7a-1c5d8e0f2a31",
                    "topicId": "deals",
                    "sendAt": "2026-11-01T09:00:00Z"
                  }
                },
                "draft": {
                  "summary": "Save a draft",
                  "value": {
                    "draft": true,
                    "title": "Autumn sale",
                    "body": "Up to 30% off"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Draft saved (`draft: true`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftSaved"
                },
                "example": {
                  "ok": true,
                  "notificationId": "5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42",
                  "status": "draft"
                }
              }
            }
          },
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendAccepted"
                },
                "example": {
                  "ok": true,
                  "notificationId": "5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42",
                  "status": "queued"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `title_required`, `unknown_field`, `external_ids_empty`, `unknown_topic`, `segment_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "title_required",
                            "unknown_field",
                            "external_ids_empty",
                            "unknown_topic",
                            "segment_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `email_not_verified`, `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "email_not_verified",
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "email_not_verified",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `too_many_drafts`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "too_many_drafts"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "too_many_drafts",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `draft_too_large`, `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "draft_too_large",
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "draft_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`, `send_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited",
                            "send_limit_reached"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/notifications \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"title\": \"Flash sale 🔥\",\n    \"body\": \"30% off ends tonight — {name|there}!\",\n    \"url\": \"https://shop.example/sale\"\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst { notificationId, status } = await rb.send({\n  title: \"Flash sale 🔥\",\n  body: \"30% off ends tonight — {name|there}!\",\n  url: \"https://shop.example/sale\",\n});"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\nres = rb.send(\n    title=\"Flash sale 🔥\",\n    body=\"30% off ends tonight — {name|there}!\",\n    url=\"https://shop.example/sale\",\n)\nprint(res[\"notificationId\"], res[\"status\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$res = $rb->send([\n    'title' => 'Flash sale 🔥',\n    'body'  => '30% off ends tonight — {name|there}!',\n    'url'   => 'https://shop.example/sale',\n]);\necho $res['notificationId'];"
          },
          {
            "lang": "Shell",
            "label": "cURL · one user",
            "source": "curl -X POST https://relaybell.com/api/v1/notifications \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"externalId\": \"user_123\",\n    \"transactional\": true,\n    \"title\": \"Your order has shipped\",\n    \"body\": \"Order #1042 is on its way.\",\n    \"url\": \"https://shop.example/orders/1042\"\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js · one user",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst { notificationId } = await rb.send({\n  externalId: \"user_123\",\n  transactional: true,\n  title: \"Your order has shipped\",\n  body: \"Order #1042 is on its way.\",\n  url: \"https://shop.example/orders/1042\",\n});"
          },
          {
            "lang": "Python",
            "label": "Python · one user",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\nres = rb.send(\n    externalId=\"user_123\",\n    transactional=True,\n    title=\"Your order has shipped\",\n    body=\"Order #1042 is on its way.\",\n    url=\"https://shop.example/orders/1042\",\n)\nprint(res[\"notificationId\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP · one user",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$res = $rb->send([\n    'externalId'    => 'user_123',\n    'transactional' => true,\n    'title'         => 'Your order has shipped',\n    'body'          => 'Order #1042 is on its way.',\n    'url'           => 'https://shop.example/orders/1042',\n]);\necho $res['notificationId'];"
          }
        ]
      },
      "get": {
        "operationId": "listNotifications",
        "tags": [
          "Notifications"
        ],
        "summary": "List notifications",
        "description": "Newest first, 25 per page (`limit` up to 100). Without `status`, everything except drafts is listed. For the next page, send the `nextCursor` you got as `cursor`, with the same filters; pages don't shift when new sends arrive.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "First day (YYYY-MM-DD, in `tz`) a notification was made on.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Last day (YYYY-MM-DD, included).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "One or more of draft, scheduled, queued, held_for_review, sending, sent, failed, canceled, separated by commas.",
            "schema": {
              "type": "string",
              "examples": [
                "sent,failed"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Searches titles and texts, in any case.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "IANA time zone for `from` / `to` (default: the website's).",
            "schema": {
              "type": "string",
              "examples": [
                "Europe/London"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–100 (default 25).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` from the previous page (send the same filters again). Anything else answers `400 invalid_cursor`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of notifications.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationList"
                },
                "example": {
                  "items": [
                    {
                      "id": "5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42",
                      "title": "Flash sale 🔥",
                      "body": "…",
                      "status": "sent",
                      "created_at": "2026-10-14T09:30:00.000Z",
                      "scheduled_at": null,
                      "recipients": 1275,
                      "sent": 1262,
                      "delivered": 1190,
                      "failed": 3,
                      "pruned": 10,
                      "clicked": 74,
                      "ctr": 5.9,
                      "conversions": 4,
                      "revenue": 316,
                      "ab": false,
                      "targetSummary": "VIP buyers · Topic: Deals",
                      "automation": false,
                      "error": null
                    }
                  ],
                  "nextCursor": null
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_status`, `invalid_range`, `invalid_tz`, `invalid_cursor`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_status",
                            "invalid_range",
                            "invalid_tz",
                            "invalid_cursor"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_status",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/notifications?status=sent&limit=25\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst page = await rb.listNotifications({ status: \"sent\", limit: 25 });\nfor (const n of page.items) console.log(n.title, n.ctr);\n// next page: rb.listNotifications({ status: \"sent\", limit: 25, cursor: page.nextCursor })"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\npage = rb.list_notifications(status=\"sent\", limit=25)\nfor n in page[\"items\"]:\n    print(n[\"title\"], n[\"ctr\"])\n# next page: rb.list_notifications(status=\"sent\", limit=25, cursor=page[\"nextCursor\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$page = $rb->listNotifications(['status' => 'sent', 'limit' => 25]);\nforeach ($page['items'] as $n) {\n    echo $n['title'], ' ', $n['ctr'], PHP_EOL;\n}"
          }
        ]
      }
    },
    "/notifications/export.csv": {
      "get": {
        "operationId": "exportNotificationsCsv",
        "tags": [
          "Notifications"
        ],
        "summary": "Export notifications as CSV",
        "description": "The same filters as the list (no cursor), the same rows in the same order, up to 10,000. Columns: id, created_at, scheduled_at, status, title, target, recipients, sent, failed, clicked, ctr, conversions, revenue. A title starting with =, +, - or @ gets a leading ' so a spreadsheet doesn't run it.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "First day (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Last day (YYYY-MM-DD, included).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated statuses.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in titles and texts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "IANA time zone for the days.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The CSV file.",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=\"relaybell-notifications[-from][-to].csv\"",
                "schema": {
                  "type": "string"
                }
              },
              "X-Row-Count": {
                "description": "Rows in the file.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                },
                "example": "id,created_at,scheduled_at,status,title,target,recipients,sent,failed,clicked,ctr,conversions,revenue\r\n"
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_status`, `invalid_range`, `invalid_tz`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_status",
                            "invalid_range",
                            "invalid_tz"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_status",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/notifications/{id}": {
      "get": {
        "operationId": "getNotification",
        "tags": [
          "Notifications"
        ],
        "summary": "Get one notification",
        "description": "Everything stored about one notification (or draft — its `sendBody` holds the saved fields), and how it did.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The notification's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The notification and its report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationDetail"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/notifications/5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst { notification, funnel } = await rb.getNotification(\"5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42\");\nconsole.log(notification.status, funnel.clicked);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\ndetail = rb.get_notification(\"5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42\")\nprint(detail[\"notification\"][\"status\"], detail[\"funnel\"][\"clicked\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$detail = $rb->getNotification('5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42');\necho $detail['notification']['status'];"
          }
        ]
      },
      "patch": {
        "operationId": "updateDraft",
        "tags": [
          "Notifications"
        ],
        "summary": "Change a draft",
        "description": "Changes the fields you send; the others stay as they were. A field sent as null is cleared. Only a draft can be changed (`409 not_a_draft`).",
        "x-scope": "send",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The draft's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendFields"
              },
              "example": {
                "title": "Autumn sale — last day"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "notificationId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "const": "draft"
                    },
                    "sendBody": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The draft's fields now."
                    }
                  },
                  "required": [
                    "ok",
                    "notificationId",
                    "status",
                    "sendBody"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `not_a_draft`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_a_draft"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_a_draft",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `draft_too_large`, `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "draft_too_large",
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "draft_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteDraft",
        "tags": [
          "Notifications"
        ],
        "summary": "Delete a draft",
        "description": "Only a draft can be deleted (`409 not_a_draft`).",
        "x-scope": "send",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The draft's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "deleted": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "ok",
                    "deleted"
                  ]
                },
                "example": {
                  "ok": true,
                  "deleted": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `not_a_draft`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_a_draft"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_a_draft",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/notifications/{id}/send": {
      "post": {
        "operationId": "sendDraft",
        "tags": [
          "Notifications"
        ],
        "summary": "Send a draft",
        "description": "Sends the draft with exactly the checks, limits, quiet hours and `sendAt` scheduling of a normal send, and answers the same way. Fields in the body replace the draft's for this send. The notification keeps its id; its date becomes the moment it was sent. A `sendAt` that has passed sends now.",
        "x-scope": "send",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The draft's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendFields"
              },
              "example": {
                "sendAt": "2026-11-01T09:00:00Z"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendAccepted"
                },
                "example": {
                  "ok": true,
                  "notificationId": "5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42",
                  "status": "scheduled"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `title_required`, `unknown_field`, `external_ids_empty`, `unknown_topic`, `segment_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "title_required",
                            "unknown_field",
                            "external_ids_empty",
                            "unknown_topic",
                            "segment_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `email_not_verified`, `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "email_not_verified",
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "email_not_verified",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `not_a_draft`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_a_draft"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_a_draft",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`, `send_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited",
                            "send_limit_reached"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/notifications/{id}/cancel": {
      "post": {
        "operationId": "cancelNotification",
        "tags": [
          "Notifications"
        ],
        "summary": "Cancel a scheduled send",
        "description": "Stops a send that is still `scheduled` (a future `sendAt`, or held for quiet hours) or `held_for_review`. A send that has already started (including a per-time-zone or smart-time send, which starts at once), has finished, was already canceled, doesn't exist or belongs to another website answers `404 not_scheduled`.",
        "x-scope": "send",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The notification id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "canceled": {
                      "type": "boolean",
                      "const": true
                    }
                  },
                  "required": [
                    "ok",
                    "canceled"
                  ]
                },
                "example": {
                  "ok": true,
                  "canceled": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_scheduled`, `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_scheduled",
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_scheduled",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/notifications/5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42/cancel \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/notifications/5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42/cancel\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/notifications/5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42/cancel\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/notifications/5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42/cancel');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/events": {
      "post": {
        "operationId": "trackEvent",
        "tags": [
          "Events"
        ],
        "summary": "Track an event",
        "description": "A behavioral event from your server: it can count as a conversion, start or cancel automations, and enter people into journeys. Name the person with `externalId` (your user id) or `subscriberId` (relaybell's id); an event that names nobody is stored, but only a product alert can come of it.\n\nProduct alerts: `back_in_stock` or `price_drop` with a `properties.productId` and **no** `externalId`, `subscriberId` or `endpoint` goes to everyone interested in that product (see [Product alerts](https://relaybell.com/docs#product-alerts)). 3,000 requests a minute per API key.",
        "x-scope": "events:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "description": "The event name (or `name`). Cut to 120 characters; missing: `400 event_required`."
                  },
                  "name": {
                    "type": "string",
                    "description": "Another spelling of `event`."
                  },
                  "externalId": {
                    "type": "string",
                    "description": "Your user id."
                  },
                  "subscriberId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "relaybell's subscriber id."
                  },
                  "endpoint": {
                    "type": "string",
                    "description": "Only disqualifies a product-alert broadcast; it does not identify the person on this route."
                  },
                  "properties": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Anything; `value` or `revenue` (a number) makes it a conversion."
                  }
                }
              },
              "examples": {
                "purchase": {
                  "summary": "A purchase",
                  "value": {
                    "event": "purchase",
                    "externalId": "user_123",
                    "properties": {
                      "value": 79.99,
                      "orderId": "A123"
                    }
                  }
                },
                "priceDrop": {
                  "summary": "A price drop (product alert)",
                  "value": {
                    "event": "price_drop",
                    "properties": {
                      "productId": "1234-42",
                      "parentId": "1234",
                      "product": "Blue Shoes",
                      "url": "https://shop.example/p/blue-shoes",
                      "oldPrice": 59,
                      "newPrice": 49
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventResult"
                },
                "example": {
                  "ok": true,
                  "eventId": "1234",
                  "subscriberId": "e1c9b7a5-3d2f-4e8b-9a6c-0f4d2b8e1a73",
                  "isConversion": true,
                  "attribution": {
                    "notificationId": "5b0c2f7e-1d7a-4c3e-9a51-2f8e6b1d0c42",
                    "value": 79.99
                  },
                  "automationsTriggered": 0,
                  "sequencesEnrolled": 0
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `event_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "event_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/events \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"event\": \"purchase\",\n    \"externalId\": \"user_123\",\n    \"properties\": {\n      \"value\": 79.99,\n      \"orderId\": \"A123\"\n    }\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst res = await rb.track(\"purchase\", {\n  externalId: \"user_123\",\n  properties: { value: 79.99, orderId: \"A123\" },\n});\nconsole.log(res.isConversion, res.attribution);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\nres = rb.track(\"purchase\", external_id=\"user_123\", properties={\"value\": 79.99, \"orderId\": \"A123\"})\nprint(res[\"isConversion\"], res[\"attribution\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$res = $rb->track('purchase', [\n    'externalId' => 'user_123',\n    'properties' => ['value' => 79.99, 'orderId' => 'A123'],\n]);"
          }
        ]
      },
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "summary": "List recent events",
        "description": "The latest 100 events, newest first. They name people, so this needs `subscribers:read`.",
        "x-scope": "subscribers:read",
        "responses": {
          "200": {
            "description": "The events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "external_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "subscriber_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "uuid"
                          },
                          "properties": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "properties",
                          "created_at"
                        ]
                      }
                    }
                  },
                  "required": [
                    "events"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/events\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/events\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/events\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/events');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/segments": {
      "get": {
        "operationId": "listSegments",
        "tags": [
          "Segments"
        ],
        "summary": "List segments",
        "description": "Saved filters, newest first.",
        "responses": {
          "200": {
            "description": "The segments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Segment"
                      }
                    }
                  },
                  "required": [
                    "segments"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/segments\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst { segments } = await rb.listSegments();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/segments\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/segments');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      },
      "post": {
        "operationId": "createSegment",
        "tags": [
          "Segments"
        ],
        "summary": "Create a segment",
        "description": "A segment needs a name and at least one complete rule (a field — a `tag:` field with its key — and a value, unless the op is `exists` / `not_exists`): a segment with no rules would include every subscriber.",
        "x-scope": "segments:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Required (`400 name_required`)."
                  },
                  "filters": {
                    "$ref": "#/components/schemas/Filters"
                  }
                },
                "required": [
                  "name",
                  "filters"
                ]
              },
              "example": {
                "name": "Indian Pro users",
                "filters": {
                  "match": "all",
                  "rules": [
                    {
                      "field": "country",
                      "op": "in",
                      "value": [
                        "IN"
                      ]
                    },
                    {
                      "field": "tag:plan",
                      "op": "eq",
                      "value": "pro"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segment": {
                      "$ref": "#/components/schemas/Segment"
                    }
                  },
                  "required": [
                    "segment"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_required`, `segment_needs_a_rule`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_required",
                            "segment_needs_a_rule"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/segments \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Indian Pro users\",\n    \"filters\": {\n      \"match\": \"all\",\n      \"rules\": [\n        {\n          \"field\": \"country\",\n          \"op\": \"in\",\n          \"value\": [\n            \"IN\"\n          ]\n        },\n        {\n          \"field\": \"tag:plan\",\n          \"op\": \"eq\",\n          \"value\": \"pro\"\n        }\n      ]\n    }\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst { segment } = await rb.createSegment({\n  name: \"Indian Pro users\",\n  filters: {\n    match: \"all\",\n    rules: [\n      { field: \"country\", op: \"in\", value: [\"IN\"] },\n      { field: \"tag:plan\", op: \"eq\", value: \"pro\" },\n    ],\n  },\n});"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/segments\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    json={\n        \"name\": \"Indian Pro users\",\n        \"filters\": {\n            \"match\": \"all\",\n            \"rules\": [\n                {\n                    \"field\": \"country\",\n                    \"op\": \"in\",\n                    \"value\": [\"IN\"],\n                },\n                {\n                    \"field\": \"tag:plan\",\n                    \"op\": \"eq\",\n                    \"value\": \"pro\",\n                },\n            ],\n        },\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/segments');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY'), 'Content-Type: application/json'],\n    CURLOPT_POSTFIELDS     => json_encode([\n        'name'    => 'Indian Pro users',\n        'filters' => [\n            'match' => 'all',\n            'rules' => [\n                [\n                    'field' => 'country',\n                    'op'    => 'in',\n                    'value' => ['IN'],\n                ],\n                [\n                    'field' => 'tag:plan',\n                    'op'    => 'eq',\n                    'value' => 'pro',\n                ],\n            ],\n        ],\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/segments/{id}": {
      "put": {
        "operationId": "updateSegment",
        "tags": [
          "Segments"
        ],
        "summary": "Change a segment",
        "description": "Changes the name and/or the filters (with the same rule check). Neither sent: `400 nothing_to_update`.",
        "x-scope": "segments:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The segment's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "filters": {
                    "$ref": "#/components/schemas/Filters"
                  }
                }
              },
              "example": {
                "name": "Indian Pro users (all devices)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The segment now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segment": {
                      "$ref": "#/components/schemas/Segment"
                    }
                  },
                  "required": [
                    "segment"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `nothing_to_update`, `segment_needs_a_rule`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "nothing_to_update",
                            "segment_needs_a_rule"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteSegment",
        "tags": [
          "Segments"
        ],
        "summary": "Delete a segment",
        "description": "While an enabled RSS feed, an enabled recurring schedule, or a scheduled or queued send uses it (to include or to exclude), it answers `409 segment_in_use` with how many of each (`feeds`, `schedules`, `pending`). An unknown id answers `{ ok: true }`.",
        "x-scope": "segments:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The segment id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `segment_in_use`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "segment_in_use"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "segment_in_use",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/segments/preview": {
      "post": {
        "operationId": "previewSegment",
        "tags": [
          "Segments"
        ],
        "summary": "Count who matches",
        "description": "How many subscribed browsers match the filters right now (a person on two devices counts twice), optionally within a topic (id or slug; an unknown one counts nobody). Works with any key.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "$ref": "#/components/schemas/Filters"
                  },
                  "topicId": {
                    "type": "string",
                    "description": "A topic id or slug."
                  }
                }
              },
              "example": {
                "filters": {
                  "match": "all",
                  "rules": [
                    {
                      "field": "country",
                      "op": "in",
                      "value": [
                        "IN"
                      ]
                    },
                    {
                      "field": "tag:plan",
                      "op": "eq",
                      "value": "pro"
                    }
                  ]
                },
                "topicId": "deals"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The count.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "count"
                  ]
                },
                "example": {
                  "count": 412
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/segments/preview \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"filters\": {\n      \"match\": \"all\",\n      \"rules\": [\n        {\n          \"field\": \"country\",\n          \"op\": \"in\",\n          \"value\": [\n            \"IN\"\n          ]\n        },\n        {\n          \"field\": \"tag:plan\",\n          \"op\": \"eq\",\n          \"value\": \"pro\"\n        }\n      ]\n    }\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/segments/preview\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"filters\": {\n      \"match\": \"all\",\n      \"rules\": [\n        {\n          \"field\": \"country\",\n          \"op\": \"in\",\n          \"value\": [\n            \"IN\"\n          ]\n        },\n        {\n          \"field\": \"tag:plan\",\n          \"op\": \"eq\",\n          \"value\": \"pro\"\n        }\n      ]\n    }\n  }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/segments/preview\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    json={\n        \"filters\": {\n            \"match\": \"all\",\n            \"rules\": [\n                {\n                    \"field\": \"country\",\n                    \"op\": \"in\",\n                    \"value\": [\"IN\"],\n                },\n                {\n                    \"field\": \"tag:plan\",\n                    \"op\": \"eq\",\n                    \"value\": \"pro\",\n                },\n            ],\n        },\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/segments/preview');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY'), 'Content-Type: application/json'],\n    CURLOPT_POSTFIELDS     => json_encode([\n        'filters' => [\n            'match' => 'all',\n            'rules' => [\n                [\n                    'field' => 'country',\n                    'op'    => 'in',\n                    'value' => ['IN'],\n                ],\n                [\n                    'field' => 'tag:plan',\n                    'op'    => 'eq',\n                    'value' => 'pro',\n                ],\n            ],\n        ],\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/audience/segments": {
      "get": {
        "operationId": "listSegmentsWithCounts",
        "tags": [
          "Segments"
        ],
        "summary": "List segments with their size",
        "description": "Your saved segments with how many subscribed devices each matches now (the same number as /segments/preview; counted for the first 50, later ones are null), when a send last went to it, and what still uses it.",
        "x-scope": "subscribers:read",
        "responses": {
          "200": {
            "description": "The segments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segments": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Segment"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "count": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "lastUsedAt": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "format": "date-time"
                              },
                              "sends": {
                                "type": "integer"
                              },
                              "usedBy": {
                                "type": "object",
                                "properties": {
                                  "feeds": {
                                    "type": "integer"
                                  },
                                  "schedules": {
                                    "type": "integer"
                                  }
                                }
                              }
                            },
                            "required": [
                              "count",
                              "lastUsedAt",
                              "sends",
                              "usedBy"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "segments"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/topics": {
      "get": {
        "operationId": "listTopics",
        "tags": [
          "Topics"
        ],
        "summary": "List topics",
        "description": "Preference-center categories, oldest first, with how many subscribers opted in to each. Subscribers opt in with `RelayBell.setTopics([\"slug\"])` or `showPreferences()`; target a send with `topicId` (id or slug).",
        "responses": {
          "200": {
            "description": "The topics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topics": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Topic"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "subscribers": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "subscribers"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "topics"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/topics\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/topics\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/topics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/topics');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      },
      "post": {
        "operationId": "createTopic",
        "tags": [
          "Topics"
        ],
        "summary": "Create a topic",
        "description": "The slug is made from the name when left out. Posting an existing slug renames that topic instead of making a second one.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Required (`400 name_required`)."
                  },
                  "slug": {
                    "type": "string",
                    "description": "Letters, digits and dashes."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Deals",
                "slug": "deals"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (or renamed).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "$ref": "#/components/schemas/Topic"
                    }
                  },
                  "required": [
                    "topic"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/topics \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Deals\",\n    \"slug\": \"deals\"\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/topics\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"name\": \"Deals\",\n    \"slug\": \"deals\"\n  }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/topics\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    json={\n        \"name\": \"Deals\",\n        \"slug\": \"deals\",\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/topics');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY'), 'Content-Type: application/json'],\n    CURLOPT_POSTFIELDS     => json_encode([\n        'name' => 'Deals',\n        'slug' => 'deals',\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/topics/{id}": {
      "put": {
        "operationId": "renameTopic",
        "tags": [
          "Topics"
        ],
        "summary": "Rename a topic",
        "description": "Only the name changes: the slug never does, since subscribers opt in by slug.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The topic's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Required (`400 name_required`)."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Deals & offers"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The topic now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "$ref": "#/components/schemas/Topic"
                    }
                  },
                  "required": [
                    "topic"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteTopic",
        "tags": [
          "Topics"
        ],
        "summary": "Delete a topic",
        "description": "Its opt-ins go with it. An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The topic id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/subscribers/export": {
      "get": {
        "operationId": "exportSubscribers",
        "tags": [
          "Subscribers"
        ],
        "summary": "Export subscribers as CSV",
        "description": "Newest first, up to 100,000. Columns: subscriber_id, external_id, country, language, timezone, status, consent_at, devices (separated by |), tags (key=value;…), created_at.",
        "x-scope": "subscribers:read",
        "responses": {
          "200": {
            "description": "The CSV file (attachment; filename=\"relaybell-subscribers.csv\").",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                },
                "example": "subscriber_id,external_id,country,language,timezone,status,consent_at,devices,tags,created_at\n"
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/subscribers/export\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst csv = await rb.export();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\ncsv_text = rb.export()"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/subscribers/export');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/subscribers/recent": {
      "get": {
        "operationId": "listRecentSubscribers",
        "tags": [
          "Subscribers"
        ],
        "summary": "List the newest subscribers",
        "description": "The newest subscribers first.",
        "x-scope": "subscribers:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–100 (default 25).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscribers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscribers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubscriberRow"
                      }
                    }
                  },
                  "required": [
                    "subscribers"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/subscribers/import": {
      "post": {
        "operationId": "importSubscribers",
        "tags": [
          "Subscribers"
        ],
        "summary": "Import or enrich subscribers",
        "description": "Creates or enriches subscribers by external id with tags and topics (by slug), up to 10,000 rows; rows without an external id are skipped. Push subscriptions themselves can't be imported: each browser must opt in on your site, and these attributes apply once it does.",
        "x-scope": "subscribers:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscribers": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "externalId": {
                          "type": "string",
                          "description": "Also accepted as external_id."
                        },
                        "tags": {
                          "type": "object",
                          "additionalProperties": {
                            "type": [
                              "string",
                              "number",
                              "boolean",
                              "null"
                            ]
                          }
                        },
                        "topics": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Topic slugs."
                        }
                      },
                      "required": [
                        "externalId"
                      ]
                    },
                    "description": "At least one (`400 no_subscribers`).",
                    "maxItems": 10000
                  }
                },
                "required": [
                  "subscribers"
                ]
              },
              "example": {
                "subscribers": [
                  {
                    "externalId": "user_123",
                    "tags": {
                      "plan": "pro",
                      "name": "Jane"
                    },
                    "topics": [
                      "deals"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "processed": {
                      "type": "integer",
                      "description": "Rows with an external id."
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "processed"
                  ]
                },
                "example": {
                  "ok": true,
                  "processed": 1,
                  "note": "Push subscriptions cannot be imported across origins; this enriches/pre-seeds subscribers by external ID."
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `no_subscribers`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "no_subscribers"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/subscribers/{externalId}/data": {
      "get": {
        "operationId": "getSubscriberData",
        "tags": [
          "Subscribers"
        ],
        "summary": "Export one subscriber's data (GDPR access)",
        "description": "Everything held about one subscriber (right of access): the stored row, tags, devices, up to 1,000 delivery events and 1,000 behavioral events (also those your server logged under their external id), and product interests.",
        "x-scope": "subscribers:read",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "Your external id for the person, or relaybell's subscriber id. Unknown: `404 not_found`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriber": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The stored subscriber row."
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "type": {
                            "type": "string"
                          },
                          "device": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "user_agent": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_seen_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "deliveryEvents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "notification_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "uuid"
                          },
                          "device": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "behavioralEvents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "properties": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "productInterests": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "product_id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "subscriber",
                    "tags",
                    "endpoints",
                    "deliveryEvents",
                    "behavioralEvents",
                    "productInterests"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/subscribers/{externalId}/profile": {
      "get": {
        "operationId": "getSubscriberProfile",
        "tags": [
          "Subscribers"
        ],
        "summary": "Get a subscriber's profile",
        "description": "One person: attributes, tags, devices, topics, their last 50 deliveries and clicks (with titles), their last 50 events, and lifetime delivered / clicked counts.",
        "x-scope": "subscribers:read",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "Your external id for the person, or relaybell's subscriber id. Unknown: `404 not_found`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriber": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "externalId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "country": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "language": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "timezone": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "createdAt": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "unsubscribedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        },
                        "consentAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "id"
                      ]
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "device": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "user_agent": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_seen_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "sends": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "notification_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "uuid"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "title": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "properties": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "stats": {
                      "type": "object",
                      "properties": {
                        "delivered": {
                          "type": "integer"
                        },
                        "clicked": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "delivered",
                        "clicked"
                      ]
                    }
                  },
                  "required": [
                    "subscriber",
                    "tags",
                    "endpoints",
                    "topics",
                    "sends",
                    "events",
                    "stats"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/subscribers/user_123/profile\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/subscribers/user_123/profile\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/subscribers/user_123/profile\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/subscribers/user_123/profile');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/subscribers/{externalId}": {
      "delete": {
        "operationId": "eraseSubscriber",
        "tags": [
          "Subscribers"
        ],
        "summary": "Erase a subscriber (GDPR erasure)",
        "description": "Permanently deletes everything about one subscriber: devices, tags, topics, journeys and their delivery and behavioral events (also those logged under their external id).",
        "x-scope": "subscribers:write",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "description": "Your external id for the person, or relaybell's subscriber id. Unknown: `404 not_found`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Erased.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "erased": {
                      "type": "boolean",
                      "const": true
                    },
                    "subscriberId": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "required": [
                    "ok",
                    "erased",
                    "subscriberId"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/audience/subscribers": {
      "get": {
        "operationId": "listAudience",
        "tags": [
          "Subscribers"
        ],
        "summary": "List and filter subscribers",
        "description": "The dashboard's subscriber list: newest first, paged by cursor, every filter narrowing the list further (they combine with AND). List filters accept a comma-separated value or repeated parameters.",
        "x-scope": "subscribers:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–100 (default 25).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` from the previous page (send the same filters again). Anything else answers `400 invalid_cursor`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "A subscriber id (exact) or part of an external id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Default all.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "subscribed",
                "unsubscribed",
                "no_device"
              ],
              "default": "all"
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "2-letter codes, comma-separated.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "\"en\" matches en, en-US, en-GB…; \"en-US\" matches only en-US.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "description": "desktop, android, ios, mobile, other (`400 invalid_device`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "Topic ids or slugs (up to 5); an unknown one matches nobody.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "`key` (has the label) or `key:value` / `key=value` (with that value); up to 10.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "joinedFrom",
            "in": "query",
            "required": false,
            "description": "Joined on or after this day (YYYY-MM-DD, in `tz`).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "joinedTo",
            "in": "query",
            "required": false,
            "description": "Joined on or before this day.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "seenFrom",
            "in": "query",
            "required": false,
            "description": "Last seen on or after this day.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "seenTo",
            "in": "query",
            "required": false,
            "description": "Last seen on or before this day.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "IANA time zone for the days (default UTC).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filters",
            "in": "query",
            "required": false,
            "description": "Segment rules as JSON, e.g. {\"match\":\"all\",\"rules\":[…]} (at most 20 rules; `400 invalid_filters`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "count",
            "in": "query",
            "required": false,
            "description": "\"1\" or \"true\" adds `total`, the number of matches.",
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of subscribers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AudienceSubscriber"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "total": {
                      "type": "integer",
                      "description": "Only with `count=1`."
                    }
                  },
                  "required": [
                    "items",
                    "nextCursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_status`, `invalid_device`, `invalid_range`, `invalid_tz`, `invalid_filters`, `invalid_cursor`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_status",
                            "invalid_device",
                            "invalid_range",
                            "invalid_tz",
                            "invalid_filters",
                            "invalid_cursor"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_status",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/audience/subscribers/facets": {
      "get": {
        "operationId": "getAudienceFacets",
        "tags": [
          "Subscribers"
        ],
        "summary": "Get filter options",
        "description": "The values this website's subscribers actually have, with counts: countries, languages, devices, labels (rb_ ones left out; up to 20 values each) and topics.",
        "x-scope": "subscribers:read",
        "responses": {
          "200": {
            "description": "The options.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "countries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "devices": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "values": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "countries",
                    "languages",
                    "devices",
                    "tags",
                    "topics"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/audience/subscribers/bulk-tag": {
      "post": {
        "operationId": "bulkTagSubscribers",
        "tags": [
          "Subscribers"
        ],
        "summary": "Label subscribers in bulk",
        "description": "Adds, changes or removes labels (tags) on up to 1,000 subscribers. At most 20 labels at a time; names are 1–64 characters, values up to 256; names starting with rb_ are reserved. Ids that aren't this website's are skipped.",
        "x-scope": "subscribers:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Subscriber ids (`400 ids_required`, `too_many_ids`, `invalid_id`).",
                    "maxItems": 1000
                  },
                  "set": {
                    "type": "object",
                    "additionalProperties": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "description": "Labels to add or change: { key: value }."
                  },
                  "remove": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Label names to remove."
                  }
                },
                "required": [
                  "ids"
                ]
              },
              "example": {
                "ids": [
                  "e1c9b7a5-3d2f-4e8b-9a6c-0f4d2b8e1a73"
                ],
                "set": {
                  "plan": "pro"
                },
                "remove": [
                  "trial"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "updated": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "ok",
                    "updated"
                  ]
                },
                "example": {
                  "ok": true,
                  "updated": 1
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `nothing_to_change`, `too_many_labels`, `invalid_label`, `reserved_label`, `ids_required`, `too_many_ids`, `invalid_id`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "nothing_to_change",
                            "too_many_labels",
                            "invalid_label",
                            "reserved_label",
                            "ids_required",
                            "too_many_ids",
                            "invalid_id"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/audience/subscribers/bulk-delete": {
      "post": {
        "operationId": "bulkDeleteSubscribers",
        "tags": [
          "Subscribers"
        ],
        "summary": "Erase subscribers in bulk",
        "description": "The same full erasure as DELETE /subscribers/{externalId}, for up to 1,000 subscribers. Ids that aren't this website's are skipped.",
        "x-scope": "subscribers:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 1000
                  }
                },
                "required": [
                  "ids"
                ]
              },
              "example": {
                "ids": [
                  "e1c9b7a5-3d2f-4e8b-9a6c-0f4d2b8e1a73"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "deleted": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "ok",
                    "deleted"
                  ]
                },
                "example": {
                  "ok": true,
                  "deleted": 1
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `ids_required`, `too_many_ids`, `invalid_id`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "ids_required",
                            "too_many_ids",
                            "invalid_id"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Your webhooks, newest first, with each one's last attempt and health, plus the event names you can subscribe to. Never the secrets.",
        "responses": {
          "200": {
            "description": "The webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "webhooks",
                    "events"
                  ]
                },
                "example": {
                  "webhooks": [
                    {
                      "id": "c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b",
                      "url": "https://yourapp.example/webhooks/relaybell",
                      "events": [
                        "notification.clicked"
                      ],
                      "enabled": true,
                      "last_status": 200,
                      "last_fired_at": "2026-10-14T09:31:12.531Z",
                      "created_at": "2026-10-01T08:00:00.000Z",
                      "consecutive_failures": 0,
                      "failing_since": null,
                      "disabled_reason": null,
                      "disabled_at": null
                    }
                  ],
                  "events": [
                    "subscriber.created",
                    "subscriber.unsubscribed",
                    "notification.sent",
                    "notification.clicked"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/webhooks\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/webhooks\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      },
      "post": {
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook",
        "description": "The url must start with http:// or https:// (`400 invalid_url`). One pointing at a private or internal address is saved, but every delivery to it is refused. An empty events list means every event; unknown names are dropped. The `secret` (whsec_…) is shown only in this answer — store it to [verify signatures](https://relaybell.com/docs#webhook-verify).",
        "x-scope": "webhooks:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Where to POST events."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "subscriber.created",
                        "subscriber.unsubscribed",
                        "notification.sent",
                        "notification.clicked"
                      ]
                    },
                    "description": "Empty or left out = every event."
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://yourapp.example/webhooks/relaybell",
                "events": [
                  "notification.clicked"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. The only time you see the secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "url": {
                          "type": "string"
                        },
                        "events": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "secret": {
                          "type": "string"
                        },
                        "enabled": {
                          "type": "boolean"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "id",
                        "url",
                        "events",
                        "secret",
                        "enabled",
                        "created_at"
                      ]
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_url`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_url"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/webhooks \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://yourapp.example/webhooks/relaybell\",\n    \"events\": [\n      \"notification.clicked\"\n    ]\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"url\": \"https://yourapp.example/webhooks/relaybell\",\n    \"events\": [\n      \"notification.clicked\"\n    ]\n  }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/webhooks\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    json={\n        \"url\": \"https://yourapp.example/webhooks/relaybell\",\n        \"events\": [\"notification.clicked\"],\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY'), 'Content-Type: application/json'],\n    CURLOPT_POSTFIELDS     => json_encode([\n        'url'    => 'https://yourapp.example/webhooks/relaybell',\n        'events' => ['notification.clicked'],\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/webhooks/{id}": {
      "put": {
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Change a webhook",
        "description": "Changes url, events and/or enabled. Turning a webhook that was off back on (`enabled: true`) clears its failure count and the reason relaybell turned it off. Nothing sent: `400 nothing_to_update`.",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Must start with http:// or https://."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                },
                "example": {
                  "webhook": {
                    "id": "c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b",
                    "url": "https://yourapp.example/webhooks/relaybell",
                    "events": [
                      "notification.clicked"
                    ],
                    "enabled": true,
                    "last_status": 200,
                    "last_fired_at": "2026-10-14T09:31:12.531Z",
                    "created_at": "2026-10-01T08:00:00.000Z",
                    "consecutive_failures": 0,
                    "failing_since": null,
                    "disabled_reason": null,
                    "disabled_at": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_url`, `nothing_to_update`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_url",
                            "nothing_to_update"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X PUT https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b \\\n  -H \"Authorization: Bearer rb_live_xxxxx\" -H \"Content-Type: application/json\" \\\n  -d '{\n    \"enabled\": true\n  }'"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b\", {\n  method: \"PUT\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"enabled\": true\n  }),\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.put(\n    \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    json={\n        \"enabled\": True,\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'PUT',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY'), 'Content-Type: application/json'],\n    CURLOPT_POSTFIELDS     => json_encode([\n        'enabled' => true,\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      },
      "delete": {
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "Removes the webhook and its delivery log. An unknown id also answers `{ ok: true }`.",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "summary": "List a webhook's deliveries",
        "description": "Every delivery and the result of its last attempt, kept for 30 days: newest first, 25 a page (`limit` up to 100).",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–100 (default 25).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` from the previous page (send the same filters again). Anything else answers `400 invalid_cursor`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "deliveries",
                    "nextCursor"
                  ]
                },
                "example": {
                  "deliveries": [
                    {
                      "id": "f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f",
                      "event": "notification.clicked",
                      "status": "succeeded",
                      "attempts": 1,
                      "last_status_code": 200,
                      "duration_ms": 182,
                      "error": null,
                      "created_at": "2026-10-14T09:31:12.345Z",
                      "last_attempt_at": "2026-10-14T09:31:12.531Z",
                      "next_attempt_at": null,
                      "delivered_at": "2026-10-14T09:31:12.531Z"
                    }
                  ],
                  "nextCursor": null
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_cursor`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_cursor"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_cursor",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries?limit=25\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries?limit=25\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    params={\n        \"limit\": \"25\",\n    },\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries?limit=25');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/webhooks/{id}/deliveries/{deliveryId}": {
      "get": {
        "operationId": "getWebhookDelivery",
        "tags": [
          "Webhooks"
        ],
        "summary": "Get one delivery",
        "description": "The delivery with the exact body that was signed and sent, the last attempt's headers (signatures included, never your secret) and the start of your server's answer.",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "description": "The delivery id (X-Relaybell-Delivery).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDeliveryDetail"
                    }
                  },
                  "required": [
                    "delivery"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f\", {\n  method: \"GET\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.get(\n    \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'GET',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/webhooks/{id}/deliveries/{deliveryId}/resend": {
      "post": {
        "operationId": "resendWebhookDelivery",
        "tags": [
          "Webhooks"
        ],
        "summary": "Resend a delivery",
        "description": "One more attempt now, with the same delivery id and body and a fresh timestamp and signature — no retries after it. A waiting retry is replaced. Resends and test events together: 30 a minute per website.",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "description": "The delivery id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  },
                  "required": [
                    "delivery"
                  ]
                },
                "example": {
                  "delivery": {
                    "id": "f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f",
                    "event": "notification.clicked",
                    "status": "pending",
                    "attempts": 1,
                    "last_status_code": 200,
                    "duration_ms": 182,
                    "error": null,
                    "created_at": "2026-10-14T09:31:12.345Z",
                    "last_attempt_at": "2026-10-14T09:31:12.531Z",
                    "next_attempt_at": null,
                    "delivered_at": "2026-10-14T09:31:12.531Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `delivery_in_progress`, `body_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "delivery_in_progress",
                            "body_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "delivery_in_progress",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f/resend \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f/resend\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f/resend\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/deliveries/f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f/resend');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "operationId": "sendWebhookTest",
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Sends a `webhook.test` event ({ webhookId, message }) to this webhook once, no retries — even when the webhook is turned off or subscribes to other events. Follow its result with the delivery routes. Shares the 30-a-minute limit with resends.",
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  },
                  "required": [
                    "delivery"
                  ]
                },
                "example": {
                  "delivery": {
                    "id": "f0e1d2c3-b4a5-4697-8869-5a4b3c2d1e0f",
                    "event": "webhook.test",
                    "status": "pending",
                    "attempts": 0,
                    "last_status_code": null,
                    "duration_ms": null,
                    "error": null,
                    "created_at": "2026-10-14T09:31:12.345Z",
                    "last_attempt_at": null,
                    "next_attempt_at": null,
                    "delivered_at": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl -X POST https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/test \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "// The Node SDK has no helper for this route: plain fetch (Node 18+).\nconst res = await fetch(\"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/test\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${process.env.RELAYBELL_API_KEY}` },\n});\nconst data = await res.json();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "# The Python SDK has no helper for this route: requests (pip install requests).\nimport os\nimport requests\n\nr = requests.post(\n    \"https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/test\",\n    headers={\"Authorization\": f\"Bearer {os.environ['RELAYBELL_API_KEY']}\"},\n    timeout=30,\n)\nr.raise_for_status()\nprint(r.json())"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // The PHP SDK has no helper for this route: the curl extension.\n$ch = curl_init('https://relaybell.com/api/v1/webhooks/c3e5a7b9-2d4f-4a6c-8e0b-1d3f5a7c9e2b/test');\ncurl_setopt_array($ch, [\n    CURLOPT_CUSTOMREQUEST  => 'POST',\n    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('RELAYBELL_API_KEY')],\n    CURLOPT_RETURNTRANSFER => true,\n]);\n$data = json_decode(curl_exec($ch), true);"
          }
        ]
      }
    },
    "/automations": {
      "get": {
        "operationId": "listAutomations",
        "tags": [
          "Automations"
        ],
        "summary": "List automations",
        "description": "Newest first, each with `sent` (all-time deliveries) and its last-30-day numbers.",
        "responses": {
          "200": {
            "description": "The automations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "automations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AutomationWithStats"
                      }
                    }
                  },
                  "required": [
                    "automations"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Create an automation",
        "description": "One push to a person when they do something: an event arrives, the optional delay passes, the message goes out. With `back_in_stock` or `price_drop` as the event it powers product alerts. Automation pushes follow your quiet hours and frequency cap.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutomationInput"
              },
              "example": {
                "name": "Cart abandon",
                "event": "cart.updated",
                "delaySeconds": 3600,
                "cancelOn": "purchase",
                "conditions": [
                  {
                    "field": "cartTotal",
                    "op": "gte",
                    "value": 20
                  }
                ],
                "action": {
                  "title": "You left {product|something} in your cart 🛒",
                  "body": "Complete your order before it's gone.",
                  "url": "{url}"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "automation": {
                      "$ref": "#/components/schemas/Automation"
                    }
                  },
                  "required": [
                    "automation"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `event_required`, `action_title_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "event_required",
                            "action_title_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/automations/{id}": {
      "get": {
        "operationId": "getAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Get an automation",
        "description": "One automation with its all-time deliveries and last-30-day numbers.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The automation's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The automation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "automation": {
                      "$ref": "#/components/schemas/AutomationWithStats"
                    }
                  },
                  "required": [
                    "automation"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Replace an automation",
        "description": "Replaces the automation with the body you send — `enabled` goes back to true unless you send false — except `conditions`, which are kept when the body has no conditions key.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The automation's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutomationInput"
              },
              "example": {
                "name": "Cart abandon",
                "event": "cart.updated",
                "delaySeconds": 7200,
                "cancelOn": "purchase",
                "action": {
                  "title": "Still thinking it over?",
                  "url": "{url}"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The automation now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "automation": {
                      "$ref": "#/components/schemas/Automation"
                    }
                  },
                  "required": [
                    "automation"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `event_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "event_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Delete an automation",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The automation id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/sequences": {
      "get": {
        "operationId": "listJourneys",
        "tags": [
          "Journeys"
        ],
        "summary": "List journeys",
        "description": "Multi-step journeys (drip sequences), newest first, with their steps, enrollments and last-30-day numbers.",
        "responses": {
          "200": {
            "description": "The journeys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sequences": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JourneyWithStats"
                      }
                    }
                  },
                  "required": [
                    "sequences"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createJourney",
        "tags": [
          "Journeys"
        ],
        "summary": "Create a journey",
        "description": "A subscriber enters when they subscribe (`trigger: \"subscribe\"`, the default) or send an event (`trigger: \"event\"` with `triggerEvent`), then moves through the steps in order; `cancelOn` takes them out. Each subscriber enters a journey once, ever. A branch step continues only for people who match its condition.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyInput"
              },
              "example": {
                "name": "Welcome series",
                "trigger": "subscribe",
                "cancelOn": "purchase",
                "steps": [
                  {
                    "delaySeconds": 0,
                    "title": "Welcome 👋",
                    "url": "https://shop.example"
                  },
                  {
                    "delaySeconds": 86400,
                    "title": "Here's how to start"
                  },
                  {
                    "type": "branch",
                    "condition": {
                      "field": "tag:plan",
                      "op": "eq",
                      "value": "free"
                    }
                  },
                  {
                    "delaySeconds": 604800,
                    "title": "A little something for you"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (the steps are in GET /sequences/{id}).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sequence": {
                      "$ref": "#/components/schemas/Journey"
                    }
                  },
                  "required": [
                    "sequence"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `trigger_event_required`, `at_least_one_step_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "trigger_event_required",
                            "at_least_one_step_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/sequences/{id}": {
      "get": {
        "operationId": "getJourney",
        "tags": [
          "Journeys"
        ],
        "summary": "Get a journey",
        "description": "The steps with their ids and 30-day numbers, and enrollments { active, completed, canceled }.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The journey's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The journey.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sequence": {
                      "$ref": "#/components/schemas/JourneyWithStats"
                    }
                  },
                  "required": [
                    "sequence"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateJourney",
        "tags": [
          "Journeys"
        ],
        "summary": "Change a journey",
        "description": "Always replaces name, enabled, trigger and cancelOn (send them all); steps change only when you send them. While nobody is in the journey you can reorder, add and remove steps (send a step's id to keep it and its numbers). While someone is in it, send every existing step back with its id, in the same order and type, and only add new steps at the end — anything else answers `409 structural_change_requires_no_active_enrollments`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The journey's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyInput"
              },
              "example": {
                "name": "Welcome series",
                "trigger": "subscribe",
                "cancelOn": "purchase",
                "enabled": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The journey now (without steps).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sequence": {
                      "$ref": "#/components/schemas/Journey"
                    }
                  },
                  "required": [
                    "sequence"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `trigger_event_required`, `at_least_one_step_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "trigger_event_required",
                            "at_least_one_step_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `structural_change_requires_no_active_enrollments`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "structural_change_requires_no_active_enrollments"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "structural_change_requires_no_active_enrollments",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteJourney",
        "tags": [
          "Journeys"
        ],
        "summary": "Delete a journey",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The journey id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/feeds": {
      "get": {
        "operationId": "listFeeds",
        "tags": [
          "RSS feeds"
        ],
        "summary": "List RSS feeds",
        "description": "RSS / Atom feeds whose new posts are sent automatically (checked about every 15 minutes).",
        "responses": {
          "200": {
            "description": "The feeds.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "feeds": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Feed"
                      }
                    }
                  },
                  "required": [
                    "feeds"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createFeed",
        "tags": [
          "RSS feeds"
        ],
        "summary": "Add an RSS feed",
        "description": "New posts in the feed are sent to everyone, or to a segment.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "http(s) feed URL (`400 invalid_url`)."
                  },
                  "segmentId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "A segment of this website (`400 invalid_segment`)."
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://blog.example/feed.xml"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "feed": {
                      "$ref": "#/components/schemas/Feed"
                    }
                  },
                  "required": [
                    "feed"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_url`, `invalid_segment`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_url",
                            "invalid_segment"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/feeds/{id}": {
      "put": {
        "operationId": "updateFeed",
        "tags": [
          "RSS feeds"
        ],
        "summary": "Change an RSS feed",
        "description": "Turns it on or off, or changes its segment (null = everyone). Nothing sent: `400 nothing_to_update`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The feed's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "segmentId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              },
              "example": {
                "enabled": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The feed now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "feed": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "url": {
                          "type": "string"
                        },
                        "enabled": {
                          "type": "boolean"
                        },
                        "segment_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uuid"
                        }
                      },
                      "required": [
                        "id",
                        "url",
                        "enabled",
                        "segment_id"
                      ]
                    }
                  },
                  "required": [
                    "feed"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `nothing_to_update`, `invalid_segment`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "nothing_to_update",
                            "invalid_segment"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteFeed",
        "tags": [
          "RSS feeds"
        ],
        "summary": "Delete an RSS feed",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The feed id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/schedules": {
      "get": {
        "operationId": "listSchedules",
        "tags": [
          "Recurring schedules"
        ],
        "summary": "List recurring campaigns",
        "description": "Newest first, with when each runs next and last ran.",
        "responses": {
          "200": {
            "description": "The schedules.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Schedule"
                      }
                    }
                  },
                  "required": [
                    "schedules"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createSchedule",
        "tags": [
          "Recurring schedules"
        ],
        "summary": "Create a recurring campaign",
        "description": "The same message every hour, day or week, at a time in a time zone, to everyone or to the people the content targets.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleInput"
              },
              "example": {
                "name": "Daily digest",
                "content": {
                  "title": "Today's picks",
                  "url": "https://shop.example/today",
                  "topicId": "deals"
                },
                "cadence": "daily",
                "atTime": "09:00",
                "timezone": "Asia/Kolkata"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedule": {
                      "$ref": "#/components/schemas/Schedule"
                    }
                  },
                  "required": [
                    "schedule"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `content_title_required`, `invalid_timezone`, `unknown_topic`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "content_title_required",
                            "invalid_timezone",
                            "unknown_topic"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/schedules/{id}": {
      "put": {
        "operationId": "updateSchedule",
        "tags": [
          "Recurring schedules"
        ],
        "summary": "Replace a recurring campaign",
        "description": "Replaces the whole schedule (fields left out take their defaults) and works out its next run again.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The schedule's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleInput"
              },
              "example": {
                "name": "Daily digest",
                "content": {
                  "title": "Today's picks"
                },
                "cadence": "weekly",
                "dayOfWeek": 1,
                "atTime": "10:00",
                "timezone": "UTC",
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The schedule now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedule": {
                      "$ref": "#/components/schemas/Schedule"
                    }
                  },
                  "required": [
                    "schedule"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `content_title_required`, `invalid_timezone`, `unknown_topic`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "content_title_required",
                            "invalid_timezone",
                            "unknown_topic"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteSchedule",
        "tags": [
          "Recurring schedules"
        ],
        "summary": "Delete a recurring campaign",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The schedule id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/templates": {
      "get": {
        "operationId": "listTemplates",
        "tags": [
          "Templates"
        ],
        "summary": "List templates",
        "description": "Saved notification copy, newest first.",
        "responses": {
          "200": {
            "description": "The templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    }
                  },
                  "required": [
                    "templates"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createTemplate",
        "tags": [
          "Templates"
        ],
        "summary": "Create a template",
        "description": "`content` is any object, e.g. a send body.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Required (`400 name_required`)."
                  },
                  "content": {
                    "type": "object",
                    "additionalProperties": true
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Sale",
                "content": {
                  "title": "Flash sale 🔥",
                  "body": "30% off ends tonight"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "$ref": "#/components/schemas/Template"
                    }
                  },
                  "required": [
                    "template"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/templates/{id}": {
      "put": {
        "operationId": "updateTemplate",
        "tags": [
          "Templates"
        ],
        "summary": "Replace a template",
        "description": "Replaces name and content.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Required (`400 name_required`)."
                  },
                  "content": {
                    "type": "object",
                    "additionalProperties": true
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Sale",
                "content": {
                  "title": "Last day of the sale"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The template now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "$ref": "#/components/schemas/Template"
                    }
                  },
                  "required": [
                    "template"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteTemplate",
        "tags": [
          "Templates"
        ],
        "summary": "Delete a template",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/goals": {
      "get": {
        "operationId": "listGoals",
        "tags": [
          "Goals"
        ],
        "summary": "List conversion goals",
        "description": "Oldest first.",
        "responses": {
          "200": {
            "description": "The goals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "goals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Goal"
                      }
                    }
                  },
                  "required": [
                    "goals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createGoal",
        "tags": [
          "Goals"
        ],
        "summary": "Create a goal",
        "description": "A goal counts one of your events. Posting an existing name changes its event instead.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Up to 80 characters."
                  },
                  "eventName": {
                    "type": "string",
                    "description": "Up to 120 characters."
                  }
                },
                "required": [
                  "name",
                  "eventName"
                ]
              },
              "example": {
                "name": "Purchases",
                "eventName": "purchase"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (or changed).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "goal": {
                      "$ref": "#/components/schemas/Goal"
                    }
                  },
                  "required": [
                    "goal"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_and_event_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_and_event_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/goals/stats": {
      "get": {
        "operationId": "getGoalStats",
        "tags": [
          "Goals"
        ],
        "summary": "Goal report and funnel",
        "description": "Over the last `days` days: unique subscribers delivered a push and who clicked, then per goal its conversions, unique converting subscribers and revenue (numeric `value` properties).",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1–365 (default 30).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "windowDays": {
                      "type": "integer"
                    },
                    "funnel": {
                      "type": "object",
                      "properties": {
                        "delivered": {
                          "type": "integer"
                        },
                        "clicked": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "delivered",
                        "clicked"
                      ]
                    },
                    "goals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          },
                          "eventName": {
                            "type": "string"
                          },
                          "conversions": {
                            "type": "integer"
                          },
                          "subscribers": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "windowDays",
                    "funnel",
                    "goals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/goals/{id}": {
      "put": {
        "operationId": "updateGoal",
        "tags": [
          "Goals"
        ],
        "summary": "Change a goal",
        "description": "Changes its name and event (both required). The name of another goal: `409 name_taken`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The goal's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "eventName": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "eventName"
                ]
              },
              "example": {
                "name": "Orders",
                "eventName": "purchase"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The goal now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "goal": {
                      "$ref": "#/components/schemas/Goal"
                    }
                  },
                  "required": [
                    "goal"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `name_and_event_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "name_and_event_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `name_taken`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "name_taken"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "name_taken",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteGoal",
        "tags": [
          "Goals"
        ],
        "summary": "Delete a goal",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The goal id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/onsite": {
      "get": {
        "operationId": "listOnsiteMessages",
        "tags": [
          "On-site messages"
        ],
        "summary": "List on-site messages",
        "description": "Banners and slide-ins the SDK shows to every visitor (the 5 newest active ones), with impressions and clicks.",
        "responses": {
          "200": {
            "description": "The messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/OnsiteMessage"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "impressions": {
                                "type": "integer"
                              },
                              "clicks": {
                                "type": "integer"
                              }
                            },
                            "required": [
                              "impressions",
                              "clicks"
                            ]
                          }
                        ]
                      }
                    }
                  },
                  "required": [
                    "messages"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createOnsiteMessage",
        "tags": [
          "On-site messages"
        ],
        "summary": "Create an on-site message",
        "description": "A banner or a slide-in on your own pages, separate from push.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnsiteInput"
              },
              "example": {
                "title": "Free shipping this week",
                "body": "On every order over $50.",
                "ctaText": "Shop now",
                "ctaUrl": "https://shop.example",
                "style": "banner"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/OnsiteMessage"
                    }
                  },
                  "required": [
                    "message"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `title_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "title_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/onsite/{id}": {
      "put": {
        "operationId": "updateOnsiteMessage",
        "tags": [
          "On-site messages"
        ],
        "summary": "Replace an on-site message",
        "description": "Replaces every field (a field left out is cleared, `active` goes back to true unless false) — except `title`, which is kept when left out.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The message's id. One that isn't this website's — or isn't a UUID — answers `404 not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnsiteInput"
              },
              "example": {
                "title": "Free shipping this week",
                "style": "banner",
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The message now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/OnsiteMessage"
                    }
                  },
                  "required": [
                    "message"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteOnsiteMessage",
        "tags": [
          "On-site messages"
        ],
        "summary": "Delete an on-site message",
        "description": "An unknown id answers `{ ok: true }`.",
        "x-scope": "settings:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The message id (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/stats": {
      "get": {
        "operationId": "getStats",
        "tags": [
          "Stats and reports"
        ],
        "summary": "Get totals and recent sends",
        "description": "Totals for the website, plus its 20 most recent sends (newest first). `subscribers` counts browsers (one person on two devices counts twice).",
        "responses": {
          "200": {
            "description": "The stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscribers": {
                      "type": "integer"
                    },
                    "subscriberDevices": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      },
                      "description": "By device: desktop, android, ios, mobile, other."
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "integer"
                        },
                        "clicks": {
                          "type": "integer"
                        },
                        "ctr": {
                          "type": "number"
                        },
                        "conversions": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "sent",
                        "clicks",
                        "ctr",
                        "conversions",
                        "revenue"
                      ]
                    },
                    "notifications": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "title": {
                            "type": "string"
                          },
                          "body": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "url": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "status": {
                            "$ref": "#/components/schemas/NotificationStatus"
                          },
                          "recipients": {
                            "type": "integer"
                          },
                          "sent": {
                            "type": "integer"
                          },
                          "failed": {
                            "type": "integer"
                          },
                          "pruned": {
                            "type": "integer"
                          },
                          "received": {
                            "type": "integer",
                            "description": "Pushes the browser confirmed it showed."
                          },
                          "clicks": {
                            "type": "integer"
                          },
                          "conversions": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number"
                          },
                          "devices": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            }
                          },
                          "ab": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "additionalProperties": true
                          },
                          "abStats": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "additionalProperties": true,
                            "description": "{ a: { delivered, clicks }, b: { delivered, clicks } } for an A/B send."
                          },
                          "error": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "scheduled_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "required": [
                          "id",
                          "title",
                          "status",
                          "created_at"
                        ]
                      }
                    },
                    "promptAb": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": true,
                      "description": "Opt-in prompt A/B results, or null."
                    },
                    "emailVerified": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    },
                    "sendingBlocked": {
                      "type": "boolean",
                      "description": "Sends are refused until the email is verified."
                    }
                  },
                  "required": [
                    "subscribers",
                    "subscriberDevices",
                    "totals",
                    "notifications",
                    "promptAb",
                    "emailVerified",
                    "sendingBlocked"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "cURL",
            "source": "curl \"https://relaybell.com/api/v1/stats\" \\\n  -H \"Authorization: Bearer rb_live_xxxxx\""
          },
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const { RelayBell } = require(\"relaybell\"); // npm install relaybell\nconst rb = new RelayBell({ apiKey: process.env.RELAYBELL_API_KEY });\n\nconst stats = await rb.stats();\nconsole.log(stats.subscribers, stats.totals.ctr);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import os\nfrom relaybell import RelayBell  # pip install relaybell\n\nrb = RelayBell(os.environ[\"RELAYBELL_API_KEY\"])\nstats = rb.stats()\nprint(stats[\"subscribers\"], stats[\"totals\"][\"ctr\"])"
          },
          {
            "lang": "PHP",
            "label": "PHP",
            "source": "<?php // composer require relaybell/relaybell-php\n$rb = new Relaybell\\Client(getenv('RELAYBELL_API_KEY'));\n\n$stats = $rb->stats();\necho $stats['subscribers'];"
          }
        ]
      }
    },
    "/stats/timeseries": {
      "get": {
        "operationId": "getTimeseries",
        "tags": [
          "Stats and reports"
        ],
        "summary": "Get daily numbers",
        "description": "One point per day (days without sends are zeros), the totals, and the same totals for the period of the same length just before. Each notification counts on the day it went out. Without `from` and `to` it covers the last 30 days; at most 366 days.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "First day (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Last day (YYYY-MM-DD, included; default today).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "IANA time zone (default: the website's, else UTC).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The series.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    },
                    "tz": {
                      "type": "string"
                    },
                    "interval": {
                      "type": "string",
                      "const": "day"
                    },
                    "points": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "sent": {
                            "type": "integer"
                          },
                          "delivered": {
                            "type": "integer"
                          },
                          "clicked": {
                            "type": "integer"
                          },
                          "conversions": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "integer"
                        },
                        "delivered": {
                          "type": "integer"
                        },
                        "clicked": {
                          "type": "integer"
                        },
                        "ctr": {
                          "type": "number"
                        },
                        "conversions": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number"
                        }
                      }
                    },
                    "previous": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string"
                        },
                        "to": {
                          "type": "string"
                        },
                        "sent": {
                          "type": "integer"
                        },
                        "delivered": {
                          "type": "integer"
                        },
                        "clicked": {
                          "type": "integer"
                        },
                        "ctr": {
                          "type": "number"
                        },
                        "conversions": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number"
                        }
                      }
                    },
                    "currency": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "from",
                    "to",
                    "tz",
                    "interval",
                    "points",
                    "totals",
                    "previous",
                    "currency"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_range`, `invalid_tz`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_range",
                            "invalid_tz"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_range",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/stats/growth": {
      "get": {
        "operationId": "getGrowth",
        "tags": [
          "Stats and reports"
        ],
        "summary": "Get subscriber growth",
        "description": "New and lost subscribers per day (days with neither are left out), oldest first.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1–365 (default 30).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The growth.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "subscribed": {
                            "type": "integer"
                          },
                          "churned": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "subscribed": {
                          "type": "integer"
                        },
                        "churned": {
                          "type": "integer"
                        },
                        "net": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "days",
                    "series",
                    "totals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/stats/health": {
      "get": {
        "operationId": "getListHealth",
        "tags": [
          "Stats and reports"
        ],
        "summary": "Get list health",
        "description": "active (not unsubscribed), reachable (devices of active subscribers), unsubscribed, new30 / lost30 (last 30 days), dormant (no delivery in 60 days, older than a week) and churnRate (%).",
        "responses": {
          "200": {
            "description": "The numbers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "active": {
                      "type": "integer"
                    },
                    "reachable": {
                      "type": "integer"
                    },
                    "unsubscribed": {
                      "type": "integer"
                    },
                    "new30": {
                      "type": "integer"
                    },
                    "lost30": {
                      "type": "integer"
                    },
                    "dormant": {
                      "type": "integer"
                    },
                    "churnRate": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "active",
                    "reachable",
                    "unsubscribed",
                    "new30",
                    "lost30",
                    "dormant",
                    "churnRate"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/reports/test": {
      "post": {
        "operationId": "sendTestReport",
        "tags": [
          "Stats and reports"
        ],
        "summary": "Email a test report now",
        "description": "Sends the performance report (last 7 days, or 30 with `frequency: \"monthly\"`) to the account's email now.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "frequency": {
                    "type": "string",
                    "enum": [
                      "weekly",
                      "monthly"
                    ]
                  }
                }
              },
              "example": {
                "frequency": "weekly"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "sentTo": {
                      "type": "string"
                    },
                    "report": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "integer"
                        },
                        "clicks": {
                          "type": "integer"
                        },
                        "ctr": {
                          "type": "number"
                        },
                        "conversions": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number"
                        },
                        "newSubs": {
                          "type": "integer"
                        },
                        "top": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "additionalProperties": true,
                          "description": "{ title, clicks } of the best send, or null."
                        }
                      }
                    }
                  },
                  "required": [
                    "ok",
                    "sentTo",
                    "report"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `no_account_email`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "no_account_email"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/me": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Website settings"
        ],
        "summary": "Get the website",
        "description": "The website the key belongs to: its id, name, VAPID public key, logo and number of stored push subscriptions.",
        "responses": {
          "200": {
            "description": "The website.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/setup-status": {
      "get": {
        "operationId": "getSetupStatus",
        "tags": [
          "Website settings"
        ],
        "summary": "Get setup progress",
        "description": "Whether the SDK has been seen on the live site, and whether there are subscribers, sends, a key and an automation.",
        "responses": {
          "200": {
            "description": "The status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installed": {
                      "type": "boolean"
                    },
                    "sdkLastSeenAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "subscribers": {
                      "type": "integer"
                    },
                    "sent": {
                      "type": "integer",
                      "description": "Notifications made (drafts not counted)."
                    },
                    "hasApiKey": {
                      "type": "boolean"
                    },
                    "hasAutomation": {
                      "type": "boolean",
                      "description": "Any automation, journey, schedule or feed."
                    },
                    "emailVerified": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    },
                    "sendingBlocked": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "installed",
                    "subscribers",
                    "sent",
                    "hasApiKey",
                    "hasAutomation",
                    "sendingBlocked"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/settings": {
      "get": {
        "operationId": "getSettings",
        "tags": [
          "Website settings"
        ],
        "summary": "Get website settings",
        "description": "A key without `full` or `settings:write` doesn't get `shopifySecret` back.",
        "responses": {
          "200": {
            "description": "The settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/Settings"
                    }
                  },
                  "required": [
                    "settings"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateSettings",
        "tags": [
          "Website settings"
        ],
        "summary": "Change website settings",
        "description": "Merges the keys you send into the stored ones (`{ \"settings\": { … } }` or the keys at the top level). `identityVerification` is ignored here — use PUT /identity.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "settings": {
                        "$ref": "#/components/schemas/Settings"
                      }
                    },
                    "required": [
                      "settings"
                    ]
                  },
                  {
                    "$ref": "#/components/schemas/Settings"
                  }
                ]
              },
              "example": {
                "settings": {
                  "frequencyCap": 3,
                  "quietHours": {
                    "enabled": true,
                    "start": "22:00",
                    "end": "08:00",
                    "timezone": "Europe/London"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The settings now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "settings": {
                      "$ref": "#/components/schemas/Settings"
                    }
                  },
                  "required": [
                    "ok",
                    "settings"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_settings`, `invalid_timezone`, `invalid_retention_days`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_settings",
                            "invalid_timezone",
                            "invalid_retention_days"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/prompt": {
      "get": {
        "operationId": "getPrompt",
        "tags": [
          "Website settings"
        ],
        "summary": "Get the opt-in prompt",
        "description": "The opt-in prompt the SDK shows.",
        "responses": {
          "200": {
            "description": "The prompt.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "prompt": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/PromptConfig"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "prompt"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updatePrompt",
        "tags": [
          "Website settings"
        ],
        "summary": "Change the opt-in prompt",
        "description": "Replaces the prompt (`{ \"prompt\": { … } }` or the fields at the top level). Only known fields are stored, with safe values.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "prompt": {
                        "$ref": "#/components/schemas/PromptConfig"
                      }
                    },
                    "required": [
                      "prompt"
                    ]
                  },
                  {
                    "$ref": "#/components/schemas/PromptConfig"
                  }
                ]
              },
              "example": {
                "prompt": {
                  "type": "slidein",
                  "trigger": "delay",
                  "delaySeconds": 5,
                  "title": "Get deals first",
                  "allowText": "Allow",
                  "accentColor": "#0B7C93"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The prompt now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "prompt": {
                      "$ref": "#/components/schemas/PromptConfig"
                    }
                  },
                  "required": [
                    "ok",
                    "prompt"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_prompt`, `invalid_color`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_prompt",
                            "invalid_color"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/welcome": {
      "get": {
        "operationId": "getWelcome",
        "tags": [
          "Website settings"
        ],
        "summary": "Get the welcome push",
        "description": "The push sent straight away to each new browser.",
        "responses": {
          "200": {
            "description": "The welcome config.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "welcome": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/WelcomeConfig"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "welcome"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateWelcome",
        "tags": [
          "Website settings"
        ],
        "summary": "Change the welcome push",
        "description": "`{ \"welcome\": { enabled, title, body, url, icon, image } }` (or the fields at the top level). It replaces the whole config.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "welcome": {
                        "$ref": "#/components/schemas/WelcomeConfig"
                      }
                    },
                    "required": [
                      "welcome"
                    ]
                  },
                  {
                    "$ref": "#/components/schemas/WelcomeConfig"
                  }
                ]
              },
              "example": {
                "welcome": {
                  "enabled": true,
                  "title": "Thanks for subscribing!",
                  "body": "We'll only send what matters.",
                  "url": "https://shop.example"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The welcome config now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "welcome": {
                      "$ref": "#/components/schemas/WelcomeConfig"
                    }
                  },
                  "required": [
                    "ok",
                    "welcome"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_welcome`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_welcome"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/logo": {
      "get": {
        "operationId": "getLogo",
        "tags": [
          "Website settings"
        ],
        "summary": "Get the default logo",
        "description": "The default notification icon.",
        "responses": {
          "200": {
            "description": "The logo.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logo": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "logo"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateLogo",
        "tags": [
          "Website settings"
        ],
        "summary": "Change the default logo",
        "description": "The image used as a notification's icon when a send has none (null removes it). Upload one with POST /upload.",
        "x-scope": "settings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "logo": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "An image URL."
                  }
                }
              },
              "example": {
                "logo": "https://relaybell.com/uploads/3f9c….png"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The logo now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "logo": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "ok",
                    "logo"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/keys": {
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "API keys"
        ],
        "summary": "List API keys",
        "description": "The website's keys (never the keys themselves) and the scopes a key can have. Needs `full` — the only scope that can list keys.",
        "x-scope": "full",
        "responses": {
          "200": {
            "description": "The keys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "keys",
                    "scopes"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Create an API key",
        "description": "**Dashboard session only** — a key can't make other keys (`403 session_required`). The answer's `apiKey` is the only time you see the key: relaybell stores only a hash. The owner gets an email about every new key.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "X-Project-Id",
            "in": "header",
            "required": true,
            "description": "The website the dashboard session acts on (or `?projectId=` / `projectId` in the body). Missing: `400 project_id_required`; not one of yours: `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Up to 60 characters (default \"API key\")."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "full",
                        "send",
                        "events:write",
                        "subscribers:read",
                        "subscribers:write",
                        "segments:write",
                        "settings:write",
                        "webhooks:write"
                      ]
                    },
                    "description": "Default [\"full\"]; a list with full becomes just [\"full\"] (`400 invalid_scopes`)."
                  },
                  "expiresAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "In the future, within 3 years, or null for none (`400 invalid_expiry`)."
                  }
                }
              },
              "example": {
                "name": "Order server",
                "scopes": [
                  "send",
                  "events:write"
                ],
                "expiresAt": null
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apiKey": {
                      "type": "string",
                      "description": "The key — shown once."
                    },
                    "key": {
                      "$ref": "#/components/schemas/ApiKey"
                    }
                  },
                  "required": [
                    "apiKey",
                    "key"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_scopes`, `invalid_expiry`, `project_id_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_scopes",
                            "invalid_expiry",
                            "project_id_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `session_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "session_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/keys/{id}": {
      "delete": {
        "operationId": "revokeApiKey",
        "tags": [
          "API keys"
        ],
        "summary": "Revoke an API key",
        "description": "The signed-in owner can revoke any key. An API key can revoke only itself (`:id` = its own id, or `current`); anything else answers `403 session_required`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The key id, or `current` for the key making the request.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `session_required`, `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "session_required",
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "session_required",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/identity": {
      "get": {
        "operationId": "getIdentity",
        "tags": [
          "Identity verification"
        ],
        "summary": "Get the identity secret",
        "description": "The secret key your server signs external ids with (`userHash` = lowercase hex HMAC-SHA256 of the external id), and whether verified ids are required. Creates the secret on the first call. Never cached.",
        "responses": {
          "200": {
            "description": "The secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string",
                      "description": "64 hex characters, used as text."
                    },
                    "required": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "secret",
                    "required"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "setIdentityRequired",
        "tags": [
          "Identity verification"
        ],
        "summary": "Require verified user ids",
        "description": "`true` turns the requirement on (and creates the secret if needed). `false` turns it off — from the dashboard only: with an API key it answers `403 identity_off_requires_dashboard`, so a leaked key can't switch the protection off.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "required": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "required"
                ]
              },
              "example": {
                "required": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The setting now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "required": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "required"
                  ]
                },
                "example": {
                  "required": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `identity_off_requires_dashboard`, `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "identity_off_requires_dashboard",
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "identity_off_requires_dashboard",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/identity/rotate": {
      "post": {
        "operationId": "rotateIdentitySecret",
        "tags": [
          "Identity verification"
        ],
        "summary": "Make a new identity secret",
        "description": "**Dashboard session only** (an API key gets `403 session_required`). The old secret stops working at once, so update your site straight away.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "X-Project-Id",
            "in": "header",
            "required": true,
            "description": "The website the dashboard session acts on (or `?projectId=` / `projectId` in the body). Missing: `400 project_id_required`; not one of yours: `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The new secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "secret"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `project_id_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_id_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_id_required",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `session_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "session_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/upload": {
      "post": {
        "operationId": "uploadImage",
        "tags": [
          "Uploads and AI"
        ],
        "summary": "Upload an image",
        "description": "An image for a notification: PNG, JPEG, GIF or WebP up to 2 MB, as the multipart form field `file`. 60 uploads an hour.",
        "x-scope": "send",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "description": "The image.",
                    "contentMediaType": "application/octet-stream"
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "The public URL of the image."
                    }
                  },
                  "required": [
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_file`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_file"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_file",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`, `insufficient_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended",
                            "insufficient_scope"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "file_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "file_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/ai/suggest": {
      "post": {
        "operationId": "suggestCopy",
        "tags": [
          "Uploads and AI"
        ],
        "summary": "Suggest notification copy",
        "description": "Three rewordings of your draft for you to edit — written by an AI model when `ai` is true, made from simple templates when it is false. 30 requests an hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "goal": {
                    "type": "string",
                    "description": "e.g. \"more clicks\"."
                  }
                }
              },
              "example": {
                "title": "New arrivals",
                "body": "Fresh styles just landed",
                "goal": "more clicks"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggestions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ai": {
                      "type": "boolean"
                    },
                    "suggestions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "title": {
                            "type": "string"
                          },
                          "body": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "ai",
                    "suggestions"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/test-devices": {
      "get": {
        "operationId": "listTestDevices",
        "tags": [
          "Test devices"
        ],
        "summary": "List test devices",
        "description": "Your own browsers marked as test devices, so \"send test to me\" never reaches real subscribers.",
        "responses": {
          "200": {
            "description": "The devices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "devices": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid",
                            "description": "The subscriber id."
                          },
                          "device": {
                            "type": "string",
                            "description": "e.g. \"Chrome on Windows\"."
                          },
                          "lastSeenAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "devices"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "addTestDevice",
        "tags": [
          "Test devices"
        ],
        "summary": "Mark a subscribed browser as a test device",
        "description": "Marks the browser with this push endpoint (it must already be subscribed to this website: `404 not_subscribed`). Up to 20 test devices (`409 too_many_test_devices`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "endpoint": {
                    "type": "string",
                    "description": "The browser's push endpoint URL."
                  }
                },
                "required": [
                  "endpoint"
                ]
              },
              "example": {
                "endpoint": "https://fcm.googleapis.com/fcm/send/…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_subscribed`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_subscribed"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_subscribed",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `too_many_test_devices`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "too_many_test_devices"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "too_many_test_devices",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/test-devices/link": {
      "post": {
        "operationId": "createTestDeviceLink",
        "tags": [
          "Test devices"
        ],
        "summary": "Get a test-device link code",
        "description": "A code valid for 24 hours, for several devices: open your site with `?rb_test=CODE` in each browser and the SDK marks it.",
        "responses": {
          "200": {
            "description": "The code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "description": "Seconds."
                    }
                  },
                  "required": [
                    "code",
                    "expiresIn"
                  ]
                },
                "example": {
                  "code": "q3Vx9TzA1bCd",
                  "expiresIn": 86400
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/test-devices/{subscriberId}": {
      "delete": {
        "operationId": "removeTestDevice",
        "tags": [
          "Test devices"
        ],
        "summary": "Remove a test device",
        "description": "Takes the test mark off (the browser stays subscribed).",
        "parameters": [
          {
            "name": "subscriberId",
            "in": "path",
            "required": true,
            "description": "The id from GET /test-devices (not a UUID: `404 not_found`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    },
    "/test-devices/send": {
      "post": {
        "operationId": "sendTestToDevices",
        "tags": [
          "Test devices"
        ],
        "summary": "Send a test to your test devices",
        "description": "Sends right now to your test devices only (it never shows up in reports). One result per push endpoint: `sent`, `expired` (removed), `key_mismatch` (the browser must subscribe again) or `failed` (with `reason` `server_auth` or `timeout`). Test sends work before the email is verified (`warning: \"email_not_verified\"`) and have their own budget of 120 per clock hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Required (`400 title_required`)."
                  },
                  "body": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "icon": {
                    "type": "string"
                  },
                  "image": {
                    "type": "string"
                  },
                  "actions": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Action"
                    }
                  },
                  "requireInteraction": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "title"
                ]
              },
              "example": {
                "title": "Test from relaybell",
                "body": "It works!",
                "url": "https://shop.example"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "How it went.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "devices": {
                      "type": "integer"
                    },
                    "sent": {
                      "type": "integer"
                    },
                    "failed": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "device": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "sent",
                              "expired",
                              "key_mismatch",
                              "failed"
                            ]
                          },
                          "code": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "description": "The push service's HTTP status."
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "server_auth",
                              "timeout"
                            ]
                          }
                        }
                      }
                    },
                    "warning": {
                      "type": "string",
                      "const": "email_not_verified"
                    }
                  },
                  "required": [
                    "devices",
                    "sent",
                    "failed",
                    "results"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `title_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "title_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `not_authenticated`, `invalid_api_key`, `api_key_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_authenticated",
                            "invalid_api_key",
                            "api_key_expired"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_authenticated",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "403": {
            "description": "Not allowed — `account_suspended`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "account_suspended"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "account_suspended",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `no_test_devices`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "no_test_devices"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "no_test_devices",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `send_limit_reached`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "send_limit_reached"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "send_limit_reached",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/config": {
      "get": {
        "operationId": "getProjectConfig",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Get the SDK config",
        "description": "What the browser SDK needs to subscribe a visitor: the VAPID public key, the opt-in prompt (with your privacy-policy link), topics and the active on-site messages. 120 requests a minute per visitor IP.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "\"wp-admin\" from the WordPress plugin's settings page (then it doesn't count as the SDK being seen on the live site).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The config.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vapidPublicKey": {
                      "type": "string"
                    },
                    "prompt": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The prompt config, plus privacyUrl; enabled is false for a suspended account."
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "onsiteMessages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "title": {
                            "type": "string"
                          },
                          "body": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "ctaText": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "ctaUrl": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "style": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "vapidPublicKey",
                    "prompt",
                    "topics",
                    "onsiteMessages"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/subscriptions": {
      "post": {
        "operationId": "subscribe",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Save a push subscription",
        "description": "Called by the SDK when a visitor allows notifications (201 for a new browser, 200 when it was already known). `externalId` links the browser to your user; with identity verification it needs a valid `userHash`, else it is ignored for this request and `identity` says `unverified`. `oldEndpoint` (from the service worker) moves a replaced subscription onto the new endpoint under the same subscriber. A new browser gets your welcome push and fires `subscriber.created`.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscription": {
                    "type": "object",
                    "properties": {
                      "endpoint": {
                        "type": "string",
                        "description": "An https push-service URL (`400 invalid_subscription` otherwise)."
                      },
                      "keys": {
                        "type": "object",
                        "properties": {
                          "p256dh": {
                            "type": "string"
                          },
                          "auth": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "required": [
                      "endpoint",
                      "keys"
                    ]
                  },
                  "externalId": {
                    "type": "string",
                    "description": "Your user id."
                  },
                  "userHash": {
                    "type": "string",
                    "description": "HMAC-SHA256 of externalId with your identity secret, lowercase hex."
                  },
                  "url": {
                    "type": "string",
                    "description": "The page the visitor is on."
                  },
                  "language": {
                    "type": "string"
                  },
                  "timezone": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "object",
                    "additionalProperties": {
                      "type": [
                        "string",
                        "number",
                        "boolean",
                        "null"
                      ]
                    },
                    "description": "rb_ keys are ignored."
                  },
                  "policyVersion": {
                    "type": "string"
                  },
                  "promptVariant": {
                    "type": "string",
                    "enum": [
                      "a",
                      "b"
                    ]
                  },
                  "oldEndpoint": {
                    "type": "string"
                  }
                },
                "required": [
                  "subscription"
                ]
              },
              "example": {
                "subscription": {
                  "endpoint": "https://fcm.googleapis.com/fcm/send/…",
                  "keys": {
                    "p256dh": "BNcRdreALRFX…",
                    "auth": "tBHItJI5svbp…"
                  }
                },
                "externalId": "jane@example.com",
                "userHash": "<signature made on your server>",
                "url": "https://shop.example/",
                "language": "en-GB"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already known: updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResult"
                }
              }
            }
          },
          "201": {
            "description": "Subscribed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResult"
                },
                "example": {
                  "ok": true,
                  "endpointId": "9d8e7f6a-5b4c-4d3e-8f2a-1b0c9d8e7f6a",
                  "identity": "verified"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_subscription`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_subscription"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "unsubscribe",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Unsubscribe a browser",
        "description": "Called by `RelayBell.unsubscribe()`: removes the endpoint, marks the subscriber unsubscribed and fires `subscriber.unsubscribed`. An unknown endpoint answers `{ ok: true, alreadyGone: true }`.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "endpoint",
            "in": "query",
            "required": false,
            "description": "The push endpoint (or in the body).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "endpoint": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "unsubscribed": {
                      "type": "boolean"
                    },
                    "alreadyGone": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                },
                "example": {
                  "ok": true,
                  "unsubscribed": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `endpoint_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "endpoint_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/events": {
      "post": {
        "operationId": "trackBrowserEvent",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Track an event from the browser",
        "description": "What `RelayBell.track()` calls. Only the browser's own push `endpoint` identifies the person here (a bare `externalId` is stored only when verified, or when no hash is sent and verification isn't required), and it never starts a product-alert broadcast.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "description": "Or `name`. Missing: `400 event_required`."
                  },
                  "name": {
                    "type": "string"
                  },
                  "properties": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "endpoint": {
                    "type": "string"
                  },
                  "externalId": {
                    "type": "string"
                  },
                  "userHash": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "event": "product.viewed",
                "properties": {
                  "productId": "1234"
                },
                "endpoint": "https://fcm.googleapis.com/fcm/send/…"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "eventId": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "eventId"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `event_required`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "event_required"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/prompt-event": {
      "post": {
        "operationId": "recordPromptShown",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Record that the prompt was shown",
        "description": "For the opt-in prompt A/B results.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "variant": {
                    "type": "string",
                    "description": "\"b\", else counted as \"a\".",
                    "enum": [
                      "a",
                      "b"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/onsite-event": {
      "post": {
        "operationId": "recordOnsiteEvent",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Record an on-site message view or click",
        "description": "An impression (default) or a click on one of the website's on-site messages; unknown messages are ignored.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "messageId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "`400 invalid_message` when missing or not a UUID."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "impression",
                      "click"
                    ],
                    "default": "impression"
                  }
                },
                "required": [
                  "messageId"
                ]
              },
              "example": {
                "messageId": "0a2c4e6f-8b0d-4c2e-a4f6-8b0d2e4f6a8c",
                "type": "click"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_message`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_message"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/inbox": {
      "get": {
        "operationId": "getInbox",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Get the public inbox",
        "description": "The 15 latest broadcasts for `showInbox()`. Transactional, automation, journey and per-person sends are never listed.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The inbox.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notifications": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "title": {
                            "type": "string"
                          },
                          "body": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "url": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "icon": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "image": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "notifications"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/projects/{projectId}/preferences": {
      "get": {
        "operationId": "getPreferences",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Get a browser's topics",
        "description": "The topic slugs the browser with this endpoint opted in to (empty for an unknown browser).",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "endpoint",
            "in": "query",
            "required": false,
            "description": "The browser's push endpoint.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The topics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "topics"
                  ]
                },
                "example": {
                  "topics": [
                    "deals"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      },
      "put": {
        "operationId": "setPreferences",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Set a browser's topics",
        "description": "Replaces the browser's topics with these slugs (unknown slugs are skipped). A browser that isn't subscribed: `404 subscriber_not_found`.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID (public: it is in every page that loads the SDK). One that is not a UUID or not a website answers `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "endpoint": {
                    "type": "string"
                  },
                  "topics": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "endpoint"
                ]
              },
              "example": {
                "endpoint": "https://fcm.googleapis.com/fcm/send/…",
                "topics": [
                  "deals",
                  "news"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "applied": {
                      "type": "integer",
                      "description": "Topics set."
                    }
                  },
                  "required": [
                    "ok",
                    "applied"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`, `subscriber_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found",
                            "subscriber_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/notifications/{id}/click": {
      "post": {
        "operationId": "recordClick",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Record a click",
        "description": "Called by relaybell's service worker when a notification is clicked; you don't call it yourself. It needs the signed token from the push payload: a missing or forged one, or an unknown notification, answers 204 and records nothing. Replays are ignored. Fires `notification.clicked`.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The notification id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "description": "The signed token (or in the body).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "ep": {
                    "type": "string",
                    "description": "The per-recipient endpoint id from the payload."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "204": {
            "description": "Ignored (no valid token, or no such notification)."
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/notifications/{id}/receipt": {
      "post": {
        "operationId": "recordReceipt",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Record a delivery receipt",
        "description": "Called by relaybell's service worker when a push reaches the device (the `delivered` count in reports); you don't call it yourself. Same signed token and replay protection as clicks.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The notification id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "description": "The signed token (or in the body).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "ep": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "204": {
            "description": "Ignored (no valid token)."
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/test-devices/claim": {
      "post": {
        "operationId": "claimTestDevice",
        "tags": [
          "Browser SDK"
        ],
        "summary": "Mark this browser as a test device",
        "description": "Called by the SDK on a page opened with `?rb_test=CODE` (from POST /test-devices/link); the code is the credential.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "code": {
                    "type": "string"
                  },
                  "endpoint": {
                    "type": "string"
                  }
                },
                "required": [
                  "projectId",
                  "code",
                  "endpoint"
                ]
              },
              "example": {
                "projectId": "8c6f1e2a-4b7d-4f3a-9e21-5d0c7b3a1f64",
                "code": "q3Vx9TzA1bCd",
                "endpoint": "https://fcm.googleapis.com/fcm/send/…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                },
                "example": {
                  "ok": true
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`, `invalid_request`, `invalid_code`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json",
                            "invalid_request",
                            "invalid_code"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `not_subscribed`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "not_subscribed"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "not_subscribed",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "409": {
            "description": "Conflict — `too_many_test_devices`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "too_many_test_devices"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "too_many_test_devices",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests — `rate_limited`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "rate_limited",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/integrations/shopify/{projectId}": {
      "post": {
        "operationId": "shopifyWebhook",
        "tags": [
          "Integrations"
        ],
        "summary": "Receive a Shopify webhook",
        "description": "Point your Shopify webhooks here. checkouts/create, checkouts/update and carts/update become `cart.updated`; orders/paid or orders/create become `purchase` (subscribe to only one); orders/fulfilled becomes `order.fulfilled`. The person is found by the customer's email (their externalId). Other topics are acknowledged and ignored.\n\nRequests must be signed with your Shopify signing secret (Settings → Privacy): a wrong `X-Shopify-Hmac-Sha256` answers `401 invalid_hmac`; without a saved secret every request is acknowledged (200) and ignored. Errors on relaybell's side also answer 200 so Shopify doesn't retry.",
        "security": [],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "description": "Your Project ID. Unknown: `404 project_not_found`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-Shopify-Hmac-Sha256",
            "in": "header",
            "required": true,
            "description": "Shopify's base64 HMAC-SHA256 of the raw body.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Shopify-Topic",
            "in": "header",
            "required": true,
            "description": "e.g. orders/paid.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Shopify-Shop-Domain",
            "in": "header",
            "required": false,
            "description": "Used for a cart link when your website address isn't set.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "The Shopify order, checkout or cart."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "event": {
                      "type": "string",
                      "description": "The relaybell event it became."
                    },
                    "ignored": {
                      "type": "string",
                      "description": "A topic that isn't mapped."
                    },
                    "error": {
                      "type": "string",
                      "const": "shopify_secret_required"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                },
                "example": {
                  "ok": true,
                  "event": "purchase"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — `invalid_json`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_json"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_json",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "401": {
            "description": "No valid credentials — `invalid_hmac`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "invalid_hmac"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "invalid_hmac",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "404": {
            "description": "Not found — `project_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "project_not_found"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "project_not_found",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          },
          "413": {
            "description": "Too large — `payload_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": "payload_too_large",
                  "request_id": "0f6e3c1a-8b2d-4e5f-9a7c-1d3b5e7f9a2c"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rb_live_…",
        "description": "A website's secret API key, from Settings → Developers → API keys. Its scopes decide what it may do (`403 insufficient_scope` with `required`); an expired key answers `401 api_key_expired`, a revoked or unknown one `401 invalid_api_key`."
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "rb_session",
        "description": "The signed-in dashboard session. Only the few routes marked \"Dashboard session only\" need it; they also need the website's id in `X-Project-Id`."
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Seconds to wait before trying again.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "A stable, machine-readable code. Branch on this, not on `message`."
          },
          "message": {
            "type": "string",
            "description": "A readable explanation, when there is one. It may change."
          },
          "request_id": {
            "type": "string",
            "description": "The same id as the `X-Request-Id` response header. Quote it when you contact support."
          }
        },
        "required": [
          "error",
          "request_id"
        ],
        "description": "Every error answer. Some carry more fields: `required` (insufficient_scope), `field` and `use` (unknown_field), `limit`, `kind` and `retryAfter` (send_limit_reached), `allowed` (invalid_scopes), `feeds`, `schedules` and `pending` (segment_in_use).",
        "additionalProperties": true
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "ok"
        ]
      },
      "Rule": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "`country` (2-letter upper-case code from the visitor's IP), `language`, `url` (the subscriber's last page), `external_id` (or `externalId`), or `tag:KEY` for one of your tags. An unknown field matches nobody.",
            "examples": [
              "country",
              "tag:plan"
            ]
          },
          "op": {
            "type": "string",
            "description": "`eq` (default; also used for an unknown op), `neq`, `contains` (case-insensitive), `in` (value is a list), `exists`, `not_exists`. For a tag, `neq` also matches subscribers without that tag.",
            "enum": [
              "eq",
              "neq",
              "contains",
              "in",
              "exists",
              "not_exists"
            ],
            "default": "eq"
          },
          "value": {
            "description": "Compared as text, exactly: `eq`, `neq` and `in` are case-sensitive (write country codes in upper case). Not needed for `exists` / `not_exists`.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          }
        },
        "required": [
          "field"
        ]
      },
      "Filters": {
        "type": "object",
        "properties": {
          "match": {
            "type": "string",
            "description": "`all` = every rule must match (default), `any` = at least one.",
            "enum": [
              "all",
              "any"
            ],
            "default": "all"
          },
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rule"
            },
            "description": "The rules."
          }
        },
        "description": "The segment filter format, shared by segments, inline send `filters` / `excludeFilters`, schedules and the audience list."
      },
      "Action": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "description": "Button id (defaults to `btn1` / `btn2`)."
          },
          "title": {
            "type": "string",
            "description": "Button text (required)."
          },
          "url": {
            "type": "string",
            "description": "Opened when the button is clicked; kept only when it is an http(s) link."
          }
        },
        "required": [
          "title"
        ]
      },
      "SendFields": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Notification title. Required to send (`400 title_required`). Supports merge tags such as `{name|there}`."
          },
          "body": {
            "type": "string",
            "description": "Message text. Supports merge tags."
          },
          "url": {
            "type": "string",
            "description": "Page opened on click. Left out, your Default link is used; your UTM tags are added. Supports merge tags."
          },
          "icon": {
            "type": "string",
            "description": "Small image (URL). Left out, your default notification logo is used."
          },
          "image": {
            "type": "string",
            "description": "Large picture (URL). Shown mainly by Chrome and Edge on Windows and Android."
          },
          "badge": {
            "type": "string",
            "description": "Small one-colour icon (URL of a PNG with a transparent background, about 96×96 px) for the Android status bar."
          },
          "actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Action"
            },
            "description": "Up to 2 buttons.",
            "maxItems": 2
          },
          "requireInteraction": {
            "type": "boolean",
            "description": "Keep the notification on screen until the person clicks or closes it (Chrome and Edge on computers)."
          },
          "tag": {
            "type": "string",
            "description": "A label: a new notification with the same tag replaces one still showing."
          },
          "externalId": {
            "type": "string",
            "description": "Send to one of your own users: the user id the browser SDK linked (`externalId` in `init()`, or `identify()`). Exact match, case matters."
          },
          "externalIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The same for a list. Used instead of `externalId` when both are sent. All empty: `400 external_ids_empty` (never everyone)."
          },
          "transactional": {
            "type": "boolean",
            "description": "A message a person is waiting for: goes out even during quiet hours, skips the frequency cap, never listed in the public inbox."
          },
          "segmentId": {
            "type": "string",
            "format": "uuid",
            "description": "Target a saved segment. Not this website's: `400 segment_not_found`."
          },
          "filters": {
            "$ref": "#/components/schemas/Filters"
          },
          "topicId": {
            "type": "string",
            "description": "Only subscribers of this topic: its id or its slug (any case). Unknown: `400 unknown_topic`."
          },
          "excludeSegmentId": {
            "type": "string",
            "format": "uuid",
            "description": "Leave this segment out. Unknown: `400 segment_not_found`."
          },
          "excludeFilters": {
            "$ref": "#/components/schemas/Filters"
          },
          "sendAt": {
            "type": "string",
            "description": "ISO 8601 time to schedule the send. In the past, or unreadable: sends now.",
            "format": "date-time"
          },
          "perTimezone": {
            "type": "boolean",
            "description": "Deliver at `localTime` in each subscriber's time zone (`sendAt` is ignored)."
          },
          "localTime": {
            "type": "string",
            "description": "\"H:MM\" or \"HH:MM\", with `perTimezone`.",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "examples": [
              "09:00"
            ]
          },
          "smartTime": {
            "type": "boolean",
            "description": "Deliver at each subscriber's most-active hour (`sendAt` is ignored)."
          },
          "ttl": {
            "type": "integer",
            "description": "Seconds the push service keeps trying an offline device. Default 24 hours, at most 28 days (2419200).",
            "minimum": 1,
            "maximum": 2419200
          },
          "translations": {
            "type": "object",
            "description": "Per-language copy keyed by two-letter language: `{ \"es\": { \"title\", \"body\" } }`.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "body": {
                  "type": "string"
                }
              }
            }
          },
          "ab": {
            "type": "object",
            "properties": {
              "b": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "image": {
                    "type": "string"
                  }
                },
                "description": "Version B; fields left out come from A."
              }
            },
            "required": [
              "b"
            ],
            "description": "A/B test: each browser gets version A (the main fields) or B. Ignored unless `b` has at least one field."
          },
          "abTest": {
            "type": "object",
            "properties": {
              "autoWinner": {
                "type": "boolean",
                "description": "Send both versions to a test group, then the winner to the rest."
              },
              "testPercent": {
                "type": "integer",
                "description": "Test group size, 1–90 (default 20).",
                "minimum": 1,
                "maximum": 90,
                "default": 20
              },
              "afterHours": {
                "type": "integer",
                "description": "Hours before the winner goes out, 1–168 (default 4).",
                "minimum": 1,
                "maximum": 168,
                "default": 4
              }
            },
            "description": "Auto-winner, together with `ab`."
          }
        },
        "description": "The fields of a send (and of a draft). Targeting: only one way of choosing people is used — `segmentId` first, then `filters`, then `externalIds` / `externalId`; `topicId` and the exclusions combine with it. No targeting = every subscriber. A snake_case targeting field (`external_id`, `segment_id`, `send_at` …) answers `400 unknown_field` instead of being ignored."
      },
      "SendRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SendFields"
          },
          {
            "type": "object",
            "properties": {
              "draft": {
                "type": "boolean",
                "description": "`true` saves a draft instead of sending (201). The title may then be empty and nothing is checked yet."
              }
            }
          }
        ]
      },
      "SendAccepted": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "notificationId": {
            "type": "string",
            "format": "uuid",
            "description": "Keep it: the send appears under this id in reports and webhooks, and it is how you cancel it."
          },
          "status": {
            "type": "string",
            "description": "`queued` (sending starts now), `scheduled` (a future `sendAt`, or held until your quiet hours end) or `held_for_review` (a new account's first large broadcast; you can cancel it meanwhile).",
            "enum": [
              "queued",
              "scheduled",
              "held_for_review"
            ]
          },
          "message": {
            "type": "string",
            "description": "Only with `held_for_review`: what happens next."
          }
        },
        "required": [
          "ok",
          "notificationId",
          "status"
        ]
      },
      "DraftSaved": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "notificationId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "const": "draft"
          }
        },
        "required": [
          "ok",
          "notificationId",
          "status"
        ]
      },
      "NotificationStatus": {
        "type": "string",
        "description": "`draft`, `scheduled`, `queued`, `held_for_review`, `sending`, `sent`, `failed` (reason in `error`) or `canceled`.",
        "enum": [
          "draft",
          "scheduled",
          "queued",
          "held_for_review",
          "sending",
          "sent",
          "failed",
          "canceled"
        ]
      },
      "NotificationListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/NotificationStatus"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a scheduled send goes (or went) out; null for one that started straight away."
          },
          "recipients": {
            "type": "integer",
            "description": "Browsers targeted."
          },
          "sent": {
            "type": "integer",
            "description": "Pushes the push services accepted."
          },
          "delivered": {
            "type": "integer",
            "description": "Pushes a device confirmed it showed."
          },
          "failed": {
            "type": "integer",
            "description": "Pushes that failed."
          },
          "pruned": {
            "type": "integer",
            "description": "Expired subscriptions found and removed during the send."
          },
          "clicked": {
            "type": "integer"
          },
          "ctr": {
            "type": "number",
            "description": "clicked ÷ sent, as a percentage with one decimal."
          },
          "conversions": {
            "type": "integer"
          },
          "revenue": {
            "type": "number"
          },
          "ab": {
            "type": "boolean",
            "description": "Whether it was an A/B test."
          },
          "targetSummary": {
            "type": "string",
            "description": "Who it went to, in words: \"All subscribers\", a segment's name, \"Topic: News\", \"Specific users\", …"
          },
          "automation": {
            "type": "boolean",
            "description": "Whether an automation, a journey or a product alert made it."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a failed send never went out (`email_not_verified`, `suspended`, `segment_missing`, `review_rejected`, `dispatch_error`), else null."
          }
        },
        "required": [
          "id",
          "title",
          "status",
          "created_at",
          "recipients",
          "sent",
          "delivered",
          "clicked",
          "ctr"
        ]
      },
      "NotificationList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationListItem"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Send as `cursor` (with the same filters) for the next page; null on the last page."
          }
        },
        "required": [
          "items",
          "nextCursor"
        ]
      },
      "NotificationDetail": {
        "type": "object",
        "properties": {
          "notification": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "title": {
                "type": "string"
              },
              "body": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "icon": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "image": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "badge": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "actions": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "$ref": "#/components/schemas/Action"
                }
              },
              "require_interaction": {
                "type": "boolean"
              },
              "tag": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/components/schemas/NotificationStatus"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "scheduled_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "target": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true,
                "description": "How the people were chosen ({ segmentId | filters, topicId, excludeSegmentId | excludeFilters, … }); null = everyone."
              },
              "targetSummary": {
                "type": "string"
              },
              "automation": {
                "type": "boolean"
              },
              "automation_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "transactional": {
                "type": "boolean"
              },
              "ab": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true
              },
              "ab_test": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true
              },
              "translations": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true
              },
              "ttl": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "per_timezone": {
                "type": "boolean"
              },
              "local_time": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "\"HH:MM\", \"smart\", or null."
              },
              "stats": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true
              },
              "error": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "recipients": {
                "type": "integer"
              },
              "processed": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "delivered": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "pruned": {
                "type": "integer"
              },
              "clicked": {
                "type": "integer"
              },
              "ctr": {
                "type": "number"
              },
              "conversions": {
                "type": "integer"
              },
              "revenue": {
                "type": "number"
              }
            },
            "required": [
              "id",
              "title",
              "status",
              "created_at"
            ]
          },
          "funnel": {
            "type": "object",
            "properties": {
              "recipients": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "delivered": {
                "type": "integer"
              },
              "clicked": {
                "type": "integer"
              },
              "converted": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "expired": {
                "type": "integer"
              }
            }
          },
          "devices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "device": {
                  "type": "string"
                },
                "sent": {
                  "type": "integer"
                },
                "clicked": {
                  "type": "integer"
                }
              }
            },
            "description": "sent and clicked by device type (empty once data retention removed the send's event log)."
          },
          "variants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "variant": {
                  "type": "string",
                  "enum": [
                    "a",
                    "b"
                  ]
                },
                "sent": {
                  "type": "integer"
                },
                "clicked": {
                  "type": "integer"
                },
                "ctr": {
                  "type": "number"
                }
              }
            },
            "description": "For an A/B send, versions a and b; otherwise empty."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "language": {
                  "type": "string",
                  "description": "Two letters, or `unknown`."
                },
                "sent": {
                  "type": "integer"
                },
                "clicked": {
                  "type": "integer"
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "What the push service answered (an HTTP status, `timeout`, …), or `expired`."
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "revenue": {
            "type": "number"
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "The website's revenue currency, or null."
          },
          "sendBody": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The POST /notifications body that makes this send again, or a draft's saved fields. null for automation, journey and product-alert sends."
          }
        },
        "required": [
          "notification",
          "funnel",
          "devices",
          "variants",
          "languages",
          "errors",
          "revenue",
          "currency",
          "sendBody"
        ]
      },
      "EventResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "eventId": {
            "type": "string",
            "description": "The stored event's id."
          },
          "subscriberId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The subscriber it was linked to, or null."
          },
          "isConversion": {
            "type": "boolean",
            "description": "purchase, order.completed, checkout.completed (or your conversionEvents), or any event with a numeric value / revenue — except cart.updated and checkout.started."
          },
          "attribution": {
            "type": [
              "object",
              "null"
            ],
            "description": "The notification credited with this conversion (last delivered in the attribution window), or null.",
            "properties": {
              "notificationId": {
                "type": "string",
                "format": "uuid"
              },
              "value": {
                "type": "number"
              }
            }
          },
          "automationsTriggered": {
            "type": "integer"
          },
          "sequencesEnrolled": {
            "type": "integer"
          },
          "productAlerts": {
            "type": "integer",
            "description": "Product alerts created (back_in_stock / price_drop naming no person)."
          },
          "alertAudience": {
            "type": "integer",
            "description": "People the product alerts reach."
          },
          "alertAudienceTruncated": {
            "type": "boolean",
            "description": "The audience was cut at 100,000."
          },
          "deduped": {
            "type": "boolean",
            "description": "A price_drop for this product already went out in the last 10 minutes."
          }
        },
        "required": [
          "ok",
          "eventId",
          "subscriberId",
          "isConversion",
          "attribution",
          "automationsTriggered",
          "sequencesEnrolled"
        ]
      },
      "Segment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "filters": {
            "$ref": "#/components/schemas/Filters"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "filters",
          "created_at"
        ]
      },
      "Topic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "Subscribers opt in by slug; it never changes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "created_at"
        ]
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty = every event."
          },
          "enabled": {
            "type": "boolean"
          },
          "last_status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "HTTP status of the last attempt (0 when no answer came back)."
          },
          "last_fired_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "consecutive_failures": {
            "type": "integer",
            "description": "Deliveries failed in a row since the last success."
          },
          "failing_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "First failure since the last success."
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when relaybell turned it off.",
            "enum": [
              "failed_50_in_a_row",
              "failing_for_3_days",
              null
            ]
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "enabled",
          "created_at",
          "consecutive_failures"
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The delivery id, also sent as X-Relaybell-Delivery (the same on every retry and resend)."
          },
          "event": {
            "type": "string",
            "examples": [
              "notification.clicked"
            ]
          },
          "status": {
            "type": "string",
            "description": "`pending` (waiting for its first attempt, or a resend / test waiting to run), `retrying`, `succeeded` (the last attempt got a 2xx) or `failed`.",
            "enum": [
              "pending",
              "retrying",
              "succeeded",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer",
            "description": "Every attempt, resends included."
          },
          "last_status_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null when no answer came back (`error` says why)."
          },
          "duration_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last attempt failed, in plain words."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the next retry runs."
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "event",
          "status",
          "attempts",
          "created_at"
        ]
      },
      "WebhookDeliveryDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookDelivery"
          },
          {
            "type": "object",
            "properties": {
              "webhook_id": {
                "type": "string",
                "format": "uuid"
              },
              "request_body": {
                "type": "string",
                "description": "Exactly what was signed and sent, up to 64 KB."
              },
              "request_truncated": {
                "type": "boolean",
                "description": "true when the body was longer than 64 KB."
              },
              "request_headers": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true,
                "description": "The last attempt's headers (content-type, user-agent, x-relaybell-event, x-relaybell-delivery and both signatures). Never your secret."
              },
              "response_body": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The first 2 KB of your server's last answer."
              }
            }
          }
        ]
      },
      "Stats30d": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "integer"
          },
          "clicks": {
            "type": "integer"
          },
          "ctr": {
            "type": "number"
          },
          "conversions": {
            "type": "integer"
          },
          "revenue": {
            "type": "number"
          }
        },
        "required": [
          "sent",
          "clicks",
          "ctr",
          "conversions",
          "revenue"
        ],
        "description": "The last 30 days, from the notifications it produced."
      },
      "Automation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "event": {
            "type": "string",
            "description": "The event name that starts it."
          },
          "conditions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "op": {
                  "type": "string",
                  "enum": [
                    "eq",
                    "neq",
                    "contains",
                    "exists",
                    "gt",
                    "gte",
                    "lt",
                    "lte"
                  ]
                },
                "value": {}
              }
            }
          },
          "action": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "icon": {
                "type": "string"
              }
            }
          },
          "delay_seconds": {
            "type": "integer"
          },
          "cancel_on": {
            "type": [
              "string",
              "null"
            ]
          },
          "once_per_subscriber": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "enabled",
          "event",
          "conditions",
          "action",
          "delay_seconds",
          "created_at"
        ]
      },
      "AutomationWithStats": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Automation"
          },
          {
            "type": "object",
            "properties": {
              "sent": {
                "type": "integer",
                "description": "All-time deliveries."
              },
              "stats30d": {
                "$ref": "#/components/schemas/Stats30d"
              }
            },
            "required": [
              "sent",
              "stats30d"
            ]
          }
        ]
      },
      "AutomationInput": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "description": "The event name that starts it (required: `400 event_required`). Up to 120 characters."
          },
          "action": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "Required (`400 action_title_required` on create)."
              },
              "body": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "icon": {
                "type": "string"
              }
            },
            "description": "The push. Merge tags take values from the event's properties: `{product}`, `{url}`, `{price|}` …"
          },
          "name": {
            "type": "string",
            "description": "Up to 120 characters (default \"Automation\")."
          },
          "delaySeconds": {
            "type": "integer",
            "description": "Wait before sending (default 0). A newer event from the same person replaces their pending one.",
            "minimum": 0
          },
          "cancelOn": {
            "type": "string",
            "description": "An event name that cancels the person's pending push, e.g. `purchase`."
          },
          "conditions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "op": {
                  "type": "string",
                  "enum": [
                    "eq",
                    "neq",
                    "contains",
                    "exists",
                    "gt",
                    "gte",
                    "lt",
                    "lte"
                  ],
                  "default": "eq"
                },
                "value": {}
              },
              "required": [
                "field"
              ]
            },
            "description": "Checked against the event's properties; all must match. On PUT, left out = kept as they are."
          },
          "oncePerSubscriber": {
            "type": "boolean",
            "description": "At most once per person, ever."
          },
          "enabled": {
            "type": "boolean",
            "description": "Default true. On PUT, it goes back to true unless you send false."
          }
        },
        "required": [
          "event"
        ]
      },
      "JourneyStep": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "sequence_id": {
            "type": "string",
            "format": "uuid"
          },
          "position": {
            "type": "integer"
          },
          "delay_seconds": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "message",
              "branch"
            ]
          },
          "action": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "icon": {
                "type": "string"
              }
            }
          },
          "condition": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "For a branch: { field, op, value }."
          },
          "stats30d": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Stats30d"
              },
              {
                "type": "null"
              }
            ],
            "description": "null for a branch step."
          }
        },
        "required": [
          "id",
          "position",
          "delay_seconds",
          "type"
        ]
      },
      "Journey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "subscribe",
              "event"
            ]
          },
          "trigger_event": {
            "type": [
              "string",
              "null"
            ]
          },
          "cancel_on": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "enabled",
          "trigger_type",
          "created_at"
        ]
      },
      "JourneyWithStats": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Journey"
          },
          {
            "type": "object",
            "properties": {
              "steps": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyStep"
                }
              },
              "enrollments": {
                "type": "object",
                "properties": {
                  "active": {
                    "type": "integer"
                  },
                  "completed": {
                    "type": "integer"
                  },
                  "canceled": {
                    "type": "integer"
                  }
                }
              },
              "stats30d": {
                "$ref": "#/components/schemas/Stats30d"
              }
            },
            "required": [
              "steps",
              "enrollments",
              "stats30d"
            ]
          }
        ]
      },
      "JourneyInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Up to 120 characters (default \"Sequence\")."
          },
          "trigger": {
            "type": "string",
            "description": "\"subscribe\" (default) or \"event\".",
            "enum": [
              "subscribe",
              "event"
            ]
          },
          "triggerEvent": {
            "type": "string",
            "description": "Required with \"event\" (`400 trigger_event_required`)."
          },
          "cancelOn": {
            "type": "string",
            "description": "An event that takes the person out of the journey, e.g. `purchase`."
          },
          "enabled": {
            "type": "boolean",
            "description": "Default true."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "An existing step's id, to keep it (and its numbers) when editing."
                },
                "type": {
                  "type": "string",
                  "description": "\"message\" (default) or \"branch\".",
                  "enum": [
                    "message",
                    "branch"
                  ]
                },
                "delaySeconds": {
                  "type": "integer",
                  "description": "The wait after the previous step (for the first step, after entering).",
                  "minimum": 0
                },
                "title": {
                  "type": "string",
                  "description": "A message step without a title is dropped."
                },
                "body": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "icon": {
                  "type": "string"
                },
                "condition": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "op": {
                      "type": "string"
                    },
                    "value": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "field"
                  ],
                  "description": "Branch: the person goes on only if they match (segment rule format)."
                }
              }
            },
            "description": "At least one (`400 at_least_one_step_required`). On PUT, steps change only when you send them."
          }
        }
      },
      "Feed": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "segment_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "last_guid": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "enabled",
          "segment_id"
        ]
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "content": {
            "type": "object",
            "additionalProperties": true,
            "description": "{ title, body, url, icon, segmentId | filters, topicId, excludeSegmentId | excludeFilters }"
          },
          "cadence": {
            "type": "string",
            "enum": [
              "hourly",
              "daily",
              "weekly"
            ]
          },
          "at_time": {
            "type": [
              "string",
              "null"
            ],
            "description": "\"HH:MM\"."
          },
          "day_of_week": {
            "type": [
              "integer",
              "null"
            ],
            "description": "0–6, 0 = Sunday."
          },
          "timezone": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "next_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "content",
          "cadence",
          "timezone",
          "enabled"
        ]
      },
      "ScheduleInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Up to 120 characters (default \"Recurring campaign\")."
          },
          "content": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "Required (`400 content_title_required`)."
              },
              "body": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "icon": {
                "type": "string"
              },
              "segmentId": {
                "type": "string",
                "format": "uuid"
              },
              "filters": {
                "$ref": "#/components/schemas/Filters"
              },
              "topicId": {
                "type": "string",
                "description": "Id or slug (`400 unknown_topic`)."
              },
              "excludeSegmentId": {
                "type": "string",
                "format": "uuid"
              },
              "excludeFilters": {
                "$ref": "#/components/schemas/Filters"
              }
            },
            "required": [
              "title"
            ]
          },
          "cadence": {
            "type": "string",
            "description": "Default daily.",
            "enum": [
              "hourly",
              "daily",
              "weekly"
            ],
            "default": "daily"
          },
          "atTime": {
            "type": "string",
            "description": "\"HH:MM\" (default \"09:00\").",
            "default": "09:00"
          },
          "dayOfWeek": {
            "type": "integer",
            "description": "0–6, 0 = Sunday (default 1).",
            "minimum": 0,
            "maximum": 6,
            "default": 1
          },
          "timezone": {
            "type": "string",
            "description": "An IANA name (default UTC; `400 invalid_timezone`).",
            "default": "UTC"
          },
          "enabled": {
            "type": "boolean",
            "description": "Default true."
          }
        },
        "required": [
          "content"
        ]
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "content": {
            "type": "object",
            "additionalProperties": true,
            "description": "Any object, e.g. a send body."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "content",
          "created_at"
        ]
      },
      "Goal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "eventName": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "eventName",
          "createdAt"
        ]
      },
      "OnsiteMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "cta_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "cta_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "style": {
            "type": "string",
            "enum": [
              "slidein",
              "banner"
            ]
          },
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "title",
          "style",
          "active",
          "created_at"
        ]
      },
      "OnsiteInput": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Up to 120 characters. Required on create (`400 title_required`); on PUT, left out keeps the old one."
          },
          "body": {
            "type": "string",
            "description": "Up to 300 characters."
          },
          "ctaText": {
            "type": "string",
            "description": "Button text, up to 40 characters."
          },
          "ctaUrl": {
            "type": "string",
            "description": "Button link, up to 500 characters."
          },
          "style": {
            "type": "string",
            "description": "Default \"slidein\".",
            "enum": [
              "slidein",
              "banner"
            ],
            "default": "slidein"
          },
          "active": {
            "type": "boolean",
            "description": "Default true."
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "prefix": {
            "type": "string",
            "description": "The visible start of the key."
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "full",
                "send",
                "events:write",
                "subscribers:read",
                "subscribers:write",
                "segments:write",
                "settings:write",
                "webhooks:write"
              ]
            }
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expired": {
            "type": "boolean"
          },
          "current": {
            "type": "boolean",
            "description": "Whether this is the key making the request."
          }
        },
        "required": [
          "id",
          "prefix",
          "scopes",
          "created_at",
          "expired",
          "current"
        ]
      },
      "Settings": {
        "type": "object",
        "properties": {
          "frequencyCap": {
            "type": "integer",
            "description": "Most pushes one browser gets in 24 hours; 0 or unset = no cap."
          },
          "quietHours": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "start": {
                "type": "string",
                "examples": [
                  "22:00"
                ]
              },
              "end": {
                "type": "string",
                "examples": [
                  "08:00"
                ]
              },
              "timezone": {
                "type": "string"
              }
            }
          },
          "defaultUrl": {
            "type": "string",
            "description": "The link used when a send has no url (\"\" or null removes it)."
          },
          "utm": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "source": {
                "type": "string"
              },
              "medium": {
                "type": "string"
              },
              "campaign": {
                "type": "string"
              }
            }
          },
          "timezone": {
            "type": "string",
            "description": "The website's time zone for reports (IANA; `400 invalid_timezone`; \"\" or null removes it)."
          },
          "retentionDays": {
            "type": "integer",
            "description": "Delete activity history older than this; 0 = keep forever (`400 invalid_retention_days`).",
            "minimum": 0,
            "maximum": 3650
          },
          "privacyUrl": {
            "type": "string",
            "description": "Linked from the opt-in prompt."
          },
          "conversionEvents": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Which events count as conversions."
          },
          "attributionWindowDays": {
            "type": "integer",
            "description": "Days after a delivery a conversion is credited to it (default 7)."
          },
          "currency": {
            "type": "string",
            "description": "Revenue currency."
          },
          "report": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "frequency": {
                "type": "string",
                "enum": [
                  "weekly",
                  "monthly"
                ]
              }
            },
            "description": "The emailed performance report."
          },
          "shopifySecret": {
            "type": "string",
            "description": "Your Shopify webhook signing secret. Not returned to a key without `full` or `settings:write`."
          },
          "identityVerification": {
            "type": "boolean",
            "description": "Read-only here; change it with PUT /identity."
          }
        },
        "additionalProperties": true,
        "description": "Website settings. Other keys are stored as they are."
      },
      "PromptConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "type": {
            "type": "string",
            "enum": [
              "slidein",
              "card",
              "bar",
              "modal",
              "native"
            ]
          },
          "trigger": {
            "type": "string",
            "enum": [
              "delay",
              "load",
              "click",
              "scroll",
              "exit"
            ]
          },
          "delaySeconds": {
            "type": "integer",
            "minimum": 0,
            "maximum": 600
          },
          "scrollPercent": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "clickSelector": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "message": {
            "type": "string",
            "maxLength": 300
          },
          "allowText": {
            "type": "string",
            "maxLength": 40
          },
          "denyText": {
            "type": "string",
            "maxLength": 40
          },
          "position": {
            "type": "string",
            "enum": [
              "bottom-right",
              "bottom-left",
              "top-right",
              "top-left",
              "top",
              "bottom"
            ]
          },
          "accentColor": {
            "type": "string",
            "description": "A color code like #0B7C93 (`400 invalid_color`).",
            "pattern": "^#[0-9a-fA-F]{3,8}$"
          },
          "iosInstallHint": {
            "type": "boolean"
          },
          "b": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "allowText": {
                "type": "string"
              }
            },
            "description": "Prompt A/B test: version B."
          }
        },
        "description": "The opt-in prompt. Only these fields are stored; unknown values fall back to the defaults."
      },
      "WelcomeConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "image": {
            "type": "string"
          }
        },
        "additionalProperties": true,
        "description": "The welcome push sent straight away to each new browser. Replaces the whole config."
      },
      "SubscriberRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "unsubscribed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "device": {
            "type": [
              "string",
              "null"
            ],
            "description": "The most recently seen device type."
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "created_at"
        ]
      },
      "AudienceSubscriber": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "subscribed",
              "unsubscribed",
              "no_device"
            ]
          },
          "device": {
            "type": [
              "string",
              "null"
            ]
          },
          "devices": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "value": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            },
            "description": "Up to 20; rb_ labels are left out."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastSeenAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "unsubscribedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status",
          "createdAt"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "vapidPublicKey": {
            "type": "string"
          },
          "logo": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscribers": {
            "type": "integer",
            "description": "Stored push subscriptions."
          }
        },
        "required": [
          "projectId"
        ]
      },
      "SubscribeResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "endpointId": {
            "type": "string",
            "format": "uuid"
          },
          "identity": {
            "type": "string",
            "description": "Only when an externalId was sent: `verified`, `unverified` (ignored for this request) or `unchecked` (no hash, verification not required).",
            "enum": [
              "verified",
              "unverified",
              "unchecked"
            ]
          }
        },
        "required": [
          "ok",
          "endpointId"
        ]
      }
    }
  }
}
