{
  "openapi": "3.1.0",
  "info": {
    "title": "SendBunny HTTP API",
    "version": "1.0.0",
    "summary": "One CloudFront host, one API key, one /v1 catalog.",
    "description": "SendBunny is a self-hosted email platform: every install runs in its own AWS account and publishes its own CloudFront URL. All API actions live under `/v1` on that host.\n\n## Authentication\n\nSend the key in the `x-api-key` header (`Authorization: Bearer` also works), created in the dashboard (API keys). The key is checked at the edge before anything runs. Keys are server-side secrets — do not ship them in client-side code.\n\n## Scopes\n\nEvery key carries a set of scopes, a set of allowed root domains, and optionally specific allowed addresses. The scopes are: `email:send`, `inbox:read`, `inbox:reply`, `inbox:manage`, `campaigns:read`, `campaigns:write`, `audience:read`, `audience:write`, `templates:read`, `templates:write`, `domains:read`, `domains:write`, `suppression:write`. A key with no scopes defaults to `email:send` only. A failed scope check returns `403 {\"error\":\"Key lacks <scope> scope\"}`.\n\n## Root scoping\n\nMost endpoints take a `root` — the registrable root domain (`send.acme.co.uk` scopes to `acme.co.uk`). A key whose allowed-domain list is empty is refused everywhere (fail-closed). Keys restricted to specific addresses additionally filter what they can read and send as.\n\n## Pagination\n\nList endpoints take `limit` (default 25, max 50 — `/stats/domain` defaults to 50) and an opaque `cursor`. Address-scoped keys are filtered after the page is fetched, so a page can return fewer items than `limit` — even zero — while `cursor` is still non-null. Keep paging until `cursor` is null.\n\n## Errors\n\nErrors are JSON: `{\"error\": \"message\"}` plus occasional context fields (`allowedRootDomains`, `missing`, `invalid`, `reason`, `email`). 401 invalid key · 403 scope or domain refusal · 404 not found or out of the key's scope · 409 wrong state · 422 suppressed recipient or unresolvable reply · 429 throttled (safe to retry) · 5xx internal.\n\nMigration: `POST /api` and `/agent/*` are gone — everything lives under `/v1`.\n\nHuman docs: https://sendbunny.co/docs  Agent index: https://sendbunny.co/llms.txt  Skill: https://sendbunny.co/docs/agents",
    "contact": {
      "url": "https://sendbunny.co/docs"
    }
  },
  "servers": [
    {
      "url": "{publicApiUrl}/v1",
      "description": "Your install's API — /v1 is appended automatically.",
      "variables": {
        "publicApiUrl": {
          "default": "https://YOUR-DISTRIBUTION.cloudfront.net",
          "description": "Just the CloudFront host from dashboard → API keys, without /v1 — it is appended automatically. Unique per install. Never an execute-api or lambda-url host."
        }
      }
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Emails",
      "description": "Transactional sending and the send ledger. POST /emails delivers one email to an array of recipients — it is not a batch API — and every send is logged for 90 days, queryable by ledger id or SES messageId. Requires the email:send scope on a key allowed for the sending root."
    },
    {
      "name": "Inbox",
      "description": "Received mail for your domains: conversations, message metadata and bodies, attachments, in-thread replies, composing from an inbox address, folder actions, inbox addresses, and turning receiving on or off per root. Reading needs inbox:read, replying needs inbox:reply, and address, folder, or receiving management needs inbox:manage. Composing from an inbox address needs email:send."
    },
    {
      "name": "Campaigns",
      "description": "Bulk sends to audience lists: create and edit drafts, send yourself a test, schedule, start, and cancel. A campaign is only editable while it is draft, scheduled, or cancelled. Reads require campaigns:read; writes require campaigns:write."
    },
    {
      "name": "Audience",
      "description": "Who you send to: lists, contacts and their list memberships, CSV imports (presign an S3 upload, then start the import job), and the per-root registry of custom contact properties. Reads require audience:read; writes require audience:write."
    },
    {
      "name": "Templates",
      "description": "Reusable email bodies. Each root domain has its own templates plus a Shared library; a send's templateAlias resolves against the sending domain first, then Shared. Sends reference a template by templateId or templateAlias. Reads require templates:read; writes require templates:write."
    },
    {
      "name": "Domains",
      "description": "Sender identities and domain health: create and verify identities, toggle click and open tracking, refresh SES verification, and read a root's sending status. Requires domains:read for reads and domains:write for changes."
    },
    {
      "name": "Suppression",
      "description": "The account-wide do-not-send list. Addresses land here from bounces, complaints, or manual adds, and POST /emails refuses suppressed recipients with a 422. Account-wide — not scoped to the key's roots. Every operation, including GET, requires suppression:write."
    },
    {
      "name": "Stats",
      "description": "Daily per-domain sending rollups over a chosen range. The API returns raw daily counter rows; rates are derived at render time. Requires domains:read."
    }
  ],
  "paths": {
    "/emails": {
      "post": {
        "tags": [
          "Emails"
        ],
        "operationId": "sendEmail",
        "summary": "Send one email",
        "description": "Sends immediately. to is always an array of recipients on one email, not a batch; to + cc together are capped at 50 addresses. Raw body XOR template. Requires email:send. From must be a VERIFIED sender identity on an allowed root. The send is logged on the ledger for 90 days (not Inbox Sent). Template aliases resolve this domain, then Shared.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              },
              "examples": {
                "raw": {
                  "summary": "Raw send",
                  "value": {
                    "from": "receipts@yourdomain.com",
                    "to": [
                      "customer@example.com"
                    ],
                    "subject": "Your order shipped",
                    "html": "<p>Order #1042 shipped today.</p>",
                    "text": "Order #1042 shipped today."
                  }
                },
                "template": {
                  "summary": "Template send",
                  "value": {
                    "from": "hello@yourdomain.com",
                    "to": [
                      "priya@example.com"
                    ],
                    "templateAlias": "password-reset",
                    "data": {
                      "name": "Priya",
                      "login_url": "https://app.example.com/reset/abc"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SES accepted the message. id is the ledger row for GET /emails/{id}.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "messageId",
                    "id"
                  ],
                  "properties": {
                    "messageId": {
                      "type": "string",
                      "description": "SES message id; empty string if SES returned none."
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Ledger row id."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed — bad addresses, both or neither of template/raw body, missing template variables (see the missing field), or invalid data values (see the invalid field).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong scope, domain not allowed for this key (allowedRootDomains lists what is), or unverified From.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Template not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A recipient is on the suppression list. reason and email identify the entry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled. Safe to retry."
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Send failed — SES rejected; the ledger row is marked failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Emails"
        ],
        "operationId": "listEmails",
        "summary": "List transactional sends for one domain",
        "description": "Requires email:send on a key allowed for the root. 90-day log, newest first; expired rows are hidden. Address-scoped keys are filtered after pagination — keep paging until cursor is null. This is the transactional ledger, not Inbox Sent.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Newest-first page of sends.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "emails": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailSummary"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/{id}": {
      "get": {
        "tags": [
          "Emails"
        ],
        "operationId": "getEmail",
        "summary": "Look up a send",
        "description": "Accepts the ledger id or the SES messageId. Requires email:send. html and text are always present in the response but stay null unless include=body is passed and the stored body still exists (bodies expire with the 90-day row). A row that exists but is outside the key's roots or addresses returns 404, never 403.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "body"
              ]
            },
            "description": "Pass body to fetch the stored html/text body from storage."
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailRecord"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks email:send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, expired, or outside the key's scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/conversations": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "listConversations",
        "summary": "List inbox conversations",
        "description": "Requires inbox:read. Newest first by last message. Address-scoped keys are filtered after pagination — keep paging until cursor is null.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "name": "folder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "INBOX",
                "SPAM",
                "SENT",
                "ARCHIVE",
                "TRASH"
              ],
              "default": "INBOX"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of conversations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationPage"
                }
              }
            }
          },
          "400": {
            "description": "'root' is required, or the folder is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong scope or root.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/conversations/{threadId}": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getConversation",
        "summary": "List messages in a thread",
        "description": "Requires inbox:read. Oldest first. limit caps the returned slice; there is no cursor on this endpoint.",
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Messages in the thread, oldest first. No cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages/{id}": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getMessage",
        "summary": "Get message metadata",
        "description": "Requires inbox:read. Metadata only — no storage keys. Fetch bodies via /messages/{id}/body and attachments via /messages/{id}/attachments/{n}.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public message projection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/Message"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found or out of scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages/{id}/body": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getMessageBody",
        "summary": "Get html and text body",
        "description": "Requires inbox:read.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Parsed body. Either field can be null.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "html": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "text": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found, or the message body is unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages/{id}/attachments/{n}": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getAttachment",
        "summary": "Download one attachment",
        "description": "Requires inbox:read. n is the 0-based index into the message's attachments array. Returns raw bytes with the attachment's Content-Type and a Content-Disposition filename — not JSON. Attachments over 4 MiB are refused with 413.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "n",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Raw bytes.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Not found, bad index, or attachment unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Attachment too large to return inline (over 4 MiB).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages/{id}/reply": {
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "replyToMessage",
        "summary": "Reply in-thread",
        "description": "Requires inbox:reply. Recipients are derived from the original message (from = the address that received it, to = the original sender, subject = Re: original) — never send to. mode:draft returns the derived envelope without sending; any other mode value sends.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "At least one of text or html is required.",
                "properties": {
                  "text": {
                    "type": "string"
                  },
                  "html": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "send"
                    ],
                    "default": "send"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft envelope (mode:draft) or the sent message ids.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/SentMessageResult"
                    },
                    {
                      "$ref": "#/components/schemas/DraftEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Provide 'text' and/or 'html', or the send failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed to send as that address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The original message has no resolvable reply envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/messages": {
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "sendInboxMessage",
        "summary": "Compose from an InboxAddress",
        "description": "Requires email:send (not an inbox scope). From must be a configured inbox address the key is allowed to use. Shows in Sent. mode:draft echoes the envelope without sending.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "to",
                  "subject"
                ],
                "description": "At least one of text or html is required.",
                "properties": {
                  "from": {
                    "type": "string",
                    "format": "email"
                  },
                  "to": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "minItems": 1
                  },
                  "cc": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "subject": {
                    "type": "string"
                  },
                  "html": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "send"
                    ],
                    "default": "send"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent message ids, or the draft envelope for mode:draft.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/SentMessageResult"
                    },
                    {
                      "$ref": "#/components/schemas/DraftEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid from, empty to, missing subject or body, or the send failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed to send from that address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/actions": {
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "inboxActions",
        "summary": "Mark read, unread, or move folder",
        "description": "Requires inbox:manage. The key must be allowed for every address in the thread — partial ownership is refused with 403. targetFolder is required for moveThreadFolder.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "threadId",
                  "folder"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "markThreadRead",
                      "markThreadUnread",
                      "moveThreadFolder"
                    ]
                  },
                  "threadId": {
                    "type": "string"
                  },
                  "folder": {
                    "type": "string",
                    "enum": [
                      "INBOX",
                      "SPAM",
                      "SENT",
                      "ARCHIVE",
                      "TRASH"
                    ],
                    "description": "The thread's current folder."
                  },
                  "targetFolder": {
                    "type": "string",
                    "description": "Destination folder for moveThreadFolder."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "result.updated counts marked messages; result.moved counts moved messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "updated": {
                          "type": "integer"
                        },
                        "moved": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad action, missing threadId, or missing/invalid folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key is not allowed to act on every address in this thread.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/addresses": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "listInboxAddresses",
        "summary": "List inbox addresses",
        "description": "Requires inbox:read. Address-scoped keys see only their own addresses.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Configured receiving addresses for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "addresses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InboxAddress"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "addInboxAddress",
        "summary": "Add an inbox address",
        "description": "Requires inbox:manage. The domain must already have receiving enabled.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "address"
                ],
                "properties": {
                  "address": {
                    "type": "string",
                    "format": "email"
                  },
                  "displayName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created address, with any provisioning warnings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "object",
                      "properties": {
                        "address": {
                          "type": "string"
                        },
                        "domain": {
                          "type": "string"
                        },
                        "displayName": {
                          "type": "string"
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A valid 'address' is required, or provisioning failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed to manage that address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/addresses/{address}": {
      "delete": {
        "tags": [
          "Inbox"
        ],
        "operationId": "deleteInboxAddress",
        "summary": "Remove an inbox address",
        "description": "Requires inbox:manage.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "400": {
            "description": "A valid address is required, or removal failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed to manage that address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/receiving": {
      "get": {
        "tags": [
          "Inbox"
        ],
        "operationId": "getInboxReceiving",
        "summary": "Get receiving status",
        "description": "Requires inbox:read. Reports the SES receiving setup for the root's apex (or its sendbunny subdomain). addresses is filtered to what the key allows.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          }
        ],
        "responses": {
          "200": {
            "description": "Receiving status for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "object",
                      "properties": {
                        "region": {
                          "type": "string"
                        },
                        "activeRuleSet": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "ruleSetIsOurs": {
                          "type": "boolean"
                        },
                        "foreignRuleSet": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "addresses": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/InboxAddress"
                          }
                        },
                        "mxRecord": {
                          "$ref": "#/components/schemas/DnsRecord"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required, or SES receiving is unavailable in the region.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No receiving identity exists for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Inbox"
        ],
        "operationId": "setInboxReceiving",
        "summary": "Enable or disable receiving",
        "description": "Requires inbox:manage. Same enable/disable as the dashboard. An omitted or unrecognized action enables receiving; only the exact value disableReceiving disables it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "root"
                ],
                "properties": {
                  "root": {
                    "type": "string"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "enableReceiving",
                      "disableReceiving"
                    ],
                    "default": "enableReceiving"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enable returns the MX record to publish plus rule-set details; disable reports the DNS records removed or skipped. Both include warnings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "domain": {
                          "type": "string"
                        },
                        "receivingEnabled": {
                          "type": "boolean"
                        },
                        "mxRecord": {
                          "$ref": "#/components/schemas/DnsRecord"
                        },
                        "route53Applied": {
                          "type": "boolean"
                        },
                        "ruleSetName": {
                          "type": "string"
                        },
                        "mergedIntoForeignRuleSet": {
                          "type": "boolean"
                        },
                        "dnsRemoved": {
                          "type": "integer"
                        },
                        "dnsSkipped": {
                          "type": "integer"
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required, or the change failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No receiving identity exists for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/campaigns": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "listCampaigns",
        "summary": "List campaigns",
        "description": "Requires campaigns:read. Newest first. Address-scoped keys are filtered after pagination — keep paging until cursor is null.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Newest-first campaign page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Campaign"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "createCampaign",
        "summary": "Create a campaign",
        "description": "Requires campaigns:write. Same fields as the dashboard composer. The list must belong to the sender's root; templateId may also reference a Shared template. Returns the raw stored row — a fresh campaign has no status or counters yet; readers should treat status as DRAFT and counters as 0 until the first GET.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created campaign (raw row; no status/counters yet).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaign": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing name/list/sender, or the list belongs to another domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root or sender address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Template not found or not usable on the root.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/campaigns/{id}": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "getCampaign",
        "summary": "Get one campaign",
        "description": "Requires campaigns:read.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign with counters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "patchCampaign",
        "summary": "Edit a campaign",
        "description": "Requires campaigns:write. Draft, scheduled, or cancelled only. Any body id is ignored — the path id wins. Clearing conventions: templateId '' removes the template, bodyJson '' removes the stored document, scheduledAt null or '' clears the schedule; omitted fields are left untouched. scheduledAt must otherwise be an ISO datetime.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditCampaignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign (raw row).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaign": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, or no updatable fields provided.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for the new sender address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Cannot edit a campaign in its current status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "deleteCampaign",
        "summary": "Delete a campaign",
        "description": "Requires campaigns:write.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/campaigns/{id}/test": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "testCampaign",
        "summary": "Send a campaign test email",
        "description": "Requires campaigns:write. Sends the stored campaign content to up to 10 recipients. Only toEmail is read from the body — it may be a single address or a comma-separated list.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "toEmail"
                ],
                "properties": {
                  "toEmail": {
                    "type": "string",
                    "description": "One address, or a comma-separated list of up to 10."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test send result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "integer"
                        },
                        "recipients": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid recipients, empty body content, or no sender.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/campaigns/{id}/start": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "startCampaign",
        "summary": "Start or schedule a campaign",
        "description": "Requires campaigns:write. With a future scheduledAt the campaign becomes SCHEDULED; otherwise it starts QUEUING immediately. A campaign that already started cannot be resent.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "QUEUING, or SCHEDULED with the scheduled time.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "campaignId": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "QUEUING",
                            "SCHEDULED"
                          ]
                        },
                        "scheduledAt": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Cannot start in the current status, or queueing failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/campaigns/{id}/cancel": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "cancelCampaign",
        "summary": "Cancel a campaign",
        "description": "Requires campaigns:write. Only SCHEDULED, QUEUING, or SENDING campaigns can be cancelled.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "campaignId": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "CANCELLED"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Campaign cannot be cancelled in its current status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/lists": {
      "get": {
        "tags": [
          "Audience"
        ],
        "operationId": "listLists",
        "summary": "List audience lists",
        "description": "Requires audience:read. Newest first.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Lists with contactCount from the rollup.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lists": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/List"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Audience"
        ],
        "operationId": "createList",
        "summary": "Create a list",
        "description": "Requires audience:write. Note the domain field in the body is named rootDomain (a missing one is reported as \"'root' query parameter is required\"). Returns the raw created row; contactCount is initialized on first use.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rootDomain",
                  "name"
                ],
                "properties": {
                  "rootDomain": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created list (raw row).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "rootDomain or name missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{id}": {
      "get": {
        "tags": [
          "Audience"
        ],
        "operationId": "getList",
        "summary": "Get one list",
        "description": "Requires audience:read.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list": {
                      "$ref": "#/components/schemas/List"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Audience"
        ],
        "operationId": "renameList",
        "summary": "Rename a list",
        "description": "Requires audience:write. Same renameList resolver as the dashboard.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed list (raw row).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'name' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Audience"
        ],
        "operationId": "deleteList",
        "summary": "Delete a list",
        "description": "Requires audience:write. Deletion cascades asynchronously — the list may remain visible briefly while memberships are removed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{id}/imports": {
      "get": {
        "tags": [
          "Audience"
        ],
        "operationId": "listImports",
        "summary": "List CSV imports for a list",
        "description": "Requires audience:read. Newest first.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Import jobs, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "imports": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ImportJob"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Audience"
        ],
        "operationId": "startImport",
        "summary": "Start a CSV import",
        "description": "Requires audience:write. The file must already be uploaded to the presigned s3Key. One import runs per list at a time. The response's job key is jobId; the listing endpoint returns the same job under id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "s3Key"
                ],
                "properties": {
                  "s3Key": {
                    "type": "string",
                    "description": "Key returned by the presign call."
                  },
                  "columnMapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Maps CSV headers to email, name, ignore, or attr:<slug>. Optional; also accepted as a JSON string."
                  },
                  "dedupPolicy": {
                    "type": "string",
                    "enum": [
                      "update",
                      "skip"
                    ],
                    "default": "update",
                    "description": "Anything other than the exact value skip means update."
                  },
                  "hasHeader": {
                    "type": "boolean",
                    "default": true
                  },
                  "totalRows": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import job accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "import": {
                      "type": "object",
                      "properties": {
                        "jobId": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "PENDING"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "s3Key missing or not under this domain's import prefix, an import is already running, or the column mapping is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{id}/imports/presign": {
      "post": {
        "tags": [
          "Audience"
        ],
        "operationId": "presignImport",
        "summary": "Presign a CSV upload",
        "description": "Requires audience:write. Returns a short-lived S3 PUT URL. The upload must match the presigned Content-Type text/csv and Content-Length bytes exactly. Max 20 MiB; the URL expires in 300 seconds.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "bytes"
                ],
                "properties": {
                  "filename": {
                    "type": "string",
                    "default": "upload.csv"
                  },
                  "bytes": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20971520
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned PUT.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "s3Key": {
                      "type": "string",
                      "description": "Pass this to the start-import call."
                    },
                    "contentType": {
                      "type": "string",
                      "enum": [
                        "text/csv"
                      ]
                    },
                    "maxBytes": {
                      "type": "integer"
                    },
                    "expiresIn": {
                      "type": "integer",
                      "description": "Seconds until the URL expires."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'bytes' must be a positive integer of at most 20 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "tags": [
          "Audience"
        ],
        "operationId": "listContacts",
        "summary": "List contacts",
        "description": "Requires audience:read. Two modes: with listId, returns that list's memberships (memberships key); with root, returns the domain's contacts (contacts key). When both are supplied, listId wins and root is ignored.",
        "parameters": [
          {
            "name": "root",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "listId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Memberships (listId mode) or domain contacts (root mode).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "List memberships",
                      "properties": {
                        "memberships": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ContactMembership"
                          }
                        },
                        "cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque cursor for the next page; null when this is the last page."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "Domain contacts",
                      "properties": {
                        "contacts": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DomainContact"
                          }
                        },
                        "cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque cursor for the next page; null when this is the last page."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Neither listId nor root supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "List not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Audience"
        ],
        "operationId": "addContact",
        "summary": "Add or update a contact",
        "description": "Requires audience:write. Note the domain field is named rootDomain. name is only written when the contact has none yet; listId also adds a list membership. Returns an outcome summary, not the contact record.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rootDomain",
                  "email"
                ],
                "properties": {
                  "rootDomain": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "name": {
                    "type": "string"
                  },
                  "listId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Outcome summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "email": {
                          "type": "string"
                        },
                        "created": {
                          "type": "boolean"
                        },
                        "addedToList": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "rootDomain missing or email invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contacts/{email}": {
      "delete": {
        "tags": [
          "Audience"
        ],
        "operationId": "deleteContact",
        "summary": "Remove a contact",
        "description": "Requires audience:write. Scoped to one domain via the root parameter. Idempotent — deleting a contact that does not exist still returns deleted: true.",
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Root"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted (idempotent).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "400": {
            "description": "A valid email and root are required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contact-properties": {
      "get": {
        "tags": [
          "Audience"
        ],
        "operationId": "listContactProperties",
        "summary": "List custom contact properties",
        "description": "Requires audience:read. Returns the full registry for the root — no pagination on this endpoint.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          }
        ],
        "responses": {
          "200": {
            "description": "Custom property registry for the root. No cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "properties": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactProperty"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Audience"
        ],
        "operationId": "addContactProperty",
        "summary": "Create a contact property",
        "description": "Requires audience:write. Note the domain field is named rootDomain. slug defaults to a slugified label; the reserved slugs email, name, company, and phone are rejected. Returns an outcome summary.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rootDomain",
                  "label",
                  "type"
                ],
                "properties": {
                  "rootDomain": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1
                  },
                  "slug": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "TEXT",
                      "NUMBER",
                      "BOOLEAN",
                      "DATE"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Outcome summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "property": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "slug": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "rootDomain, label, or type missing; slug invalid, reserved, or already taken.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "listTemplates",
        "summary": "List templates",
        "description": "Requires templates:read. Domain templates plus Shared library rows the domain has not overridden. The cursor advances the domain-scoped half only; Shared entries are appended to every page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Templates for the root plus Shared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Templates"
        ],
        "operationId": "createTemplate",
        "summary": "Create a template",
        "description": "Requires templates:write. alias must match [a-z0-9][a-z0-9_-]{0,63} and be unique on the domain; when omitted it is derived from the name. Returns the raw created row.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created template (raw row).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "rootDomain, name, subject, or htmlBody missing; alias invalid or taken.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/templates/{id}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "operationId": "getTemplate",
        "summary": "Get one template",
        "description": "Requires templates:read. Shared templates are readable by every key.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template including htmlBody.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "$ref": "#/components/schemas/TemplateWithBody"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Templates"
        ],
        "operationId": "editTemplate",
        "summary": "Edit a template",
        "description": "Requires templates:write. Any body id is ignored — the path id wins. alias '' clears the alias; bodyJson '' clears the stored document; omitted fields are left untouched.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated template (raw row).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Templates"
        ],
        "operationId": "deleteTemplate",
        "summary": "Delete a template",
        "description": "Requires templates:write. Shared templates cannot be deleted through /v1 — they 404.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identities": {
      "get": {
        "tags": [
          "Domains"
        ],
        "operationId": "listIdentities",
        "summary": "List sender identities",
        "description": "Requires domains:read.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Sender identities for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Identity"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "operationId": "createIdentity",
        "summary": "Create or verify a sender identity",
        "description": "Requires domains:write for every action, including get. action create returns the DNS records to publish (and applies them via Route 53 when hostedZoneId is given); get returns the stored identity without calling SES (use /identities/{identity}/refresh for live status plus DNS records); delete removes the identity and reports the DNS cleanup counts. Any other action is rejected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "identity"
                ],
                "properties": {
                  "identity": {
                    "type": "string",
                    "description": "Domain or email address to register as a sender."
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "create",
                      "get",
                      "delete"
                    ],
                    "default": "create"
                  },
                  "hostedZoneId": {
                    "type": "string",
                    "description": "Route 53 zone to auto-apply DNS records into."
                  },
                  "forceRefresh": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "create: identity, status, verified, dnsRecords, mailFrom, route53Applied, recordsApplied, dmarcCreated, route53Managed, warnings. get: the stored identity (Identity shape). delete: deleted, dnsRemoved, dnsSkipped.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "description": "Shape depends on action — see the operation description."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'identity' is required, or the action is not create/get/delete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "action get: no such identity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identities/{identity}/tracking": {
      "patch": {
        "tags": [
          "Domains"
        ],
        "operationId": "setIdentityTracking",
        "summary": "Set click and open tracking",
        "description": "Requires domains:write. Both booleans are required so a missing flag cannot turn tracking off. Note the read shape reports these as clickTracking/openTracking (no Enabled suffix).",
        "parameters": [
          {
            "name": "identity",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clickTrackingEnabled",
                  "openTrackingEnabled"
                ],
                "properties": {
                  "clickTrackingEnabled": {
                    "type": "boolean"
                  },
                  "openTrackingEnabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated tracking flags.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "identity": {
                          "type": "string"
                        },
                        "clickTrackingEnabled": {
                          "type": "boolean"
                        },
                        "openTrackingEnabled": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Both flags are required booleans.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identities/{identity}/refresh": {
      "post": {
        "tags": [
          "Domains"
        ],
        "operationId": "refreshIdentity",
        "summary": "Refresh verification status",
        "description": "Requires domains:write. Re-checks SES and returns the latest status with the DNS records. Throttled to one SES check per 15 seconds per identity — inside the window the cached row is served.",
        "parameters": [
          {
            "name": "identity",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Latest SES status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "identity": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "verified": {
                          "type": "boolean"
                        },
                        "dnsRecords": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DnsRecord"
                          }
                        },
                        "mailFrom": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "domain": {
                              "type": "string"
                            },
                            "status": {
                              "type": "string"
                            }
                          }
                        },
                        "route53Managed": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identities/{identity}": {
      "delete": {
        "tags": [
          "Domains"
        ],
        "operationId": "deleteIdentity",
        "summary": "Delete a sender identity",
        "description": "Requires domains:write. Refused while inbox addresses still exist on the identity.",
        "parameters": [
          {
            "name": "identity",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "400": {
            "description": "Not a deletable identity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/domains/{root}/status": {
      "get": {
        "tags": [
          "Domains"
        ],
        "operationId": "getDomainStatus",
        "summary": "Get sending status for a root",
        "description": "Requires domains:read. tenantName and sendingStatus are null when no SES tenant exists for the root yet.",
        "parameters": [
          {
            "name": "root",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sending status for the root.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "object",
                      "properties": {
                        "rootDomain": {
                          "type": "string"
                        },
                        "tenantName": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "sendingStatus": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "raw": {
                          "type": "string",
                          "description": "Raw SES SendingStatus, when a tenant exists."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/suppression": {
      "get": {
        "tags": [
          "Suppression"
        ],
        "operationId": "listSuppression",
        "summary": "Browse the suppression list",
        "description": "Requires suppression:write (there is no suppression:read). Account-wide, matching the dashboard — not scoped to the key's roots. Newest first.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Newest-first suppression entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SuppressionEntry"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Key lacks suppression:write scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Suppression"
        ],
        "operationId": "addSuppression",
        "summary": "Suppress an address",
        "description": "Requires suppression:write. API adds are always stored with reason MANUAL and synced to the SES account-level suppression list. Returns an outcome summary.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entry": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "email": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A valid email is required, or the note exceeds 500 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks suppression:write scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/suppression/{email}": {
      "delete": {
        "tags": [
          "Suppression"
        ],
        "operationId": "removeSuppression",
        "summary": "Remove a suppression entry",
        "description": "Requires suppression:write. Also removed from SES when SendBunny owns the entry.",
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "400": {
            "description": "A valid email is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks suppression:write scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stats/domain": {
      "get": {
        "tags": [
          "Stats"
        ],
        "operationId": "getDomainStats",
        "summary": "Get daily domain stats",
        "description": "Requires domains:read. One row per day per source, ascending by date. Counters are raw — derive rates client-side. limit on this endpoint defaults to 50 (max 50).",
        "parameters": [
          {
            "$ref": "#/components/parameters/Root"
          },
          {
            "name": "range",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "14d",
                "1m",
                "3m",
                "1y",
                "5y"
              ],
              "default": "14d"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Daily rollup rows, ascending by date.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "root": {
                      "type": "string"
                    },
                    "range": {
                      "type": "string"
                    },
                    "since": {
                      "type": "string",
                      "description": "First day of the range, YYYY-MM-DD."
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque cursor for the next page; null when this is the last page."
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StatsRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "'root' is required, or the range is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not allowed for that root domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Dashboard API key, checked at the edge before anything in your account runs. Starts with sb. Carries scopes plus allowed root domains and optional addresses. `Authorization: Bearer <key>` is accepted too. Server-side secret, do not ship it in client-side code."
      }
    },
    "parameters": {
      "Root": {
        "name": "root",
        "in": "query",
        "required": true,
        "description": "The sending root domain the key is scoped to, e.g. yourdomain.com.",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Page size. Default 25, max 50.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "default": 25
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Opaque cursor from the previous page."
      }
    },
    "schemas": {
      "SendEmailRequest": {
        "description": "Exactly one of the two send modes.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/RawSendRequest"
          },
          {
            "$ref": "#/components/schemas/TemplateSendRequest"
          }
        ]
      },
      "RawSendRequest": {
        "type": "object",
        "required": [
          "from",
          "to",
          "subject"
        ],
        "anyOf": [
          {
            "required": [
              "html"
            ]
          },
          {
            "required": [
              "text"
            ]
          }
        ],
        "not": {
          "anyOf": [
            {
              "required": [
                "templateId"
              ]
            },
            {
              "required": [
                "templateAlias"
              ]
            },
            {
              "required": [
                "data"
              ]
            }
          ]
        },
        "properties": {
          "from": {
            "$ref": "#/components/schemas/FromAddress"
          },
          "to": {
            "$ref": "#/components/schemas/ToAddresses"
          },
          "cc": {
            "$ref": "#/components/schemas/ToAddresses"
          },
          "subject": {
            "type": "string",
            "minLength": 1
          },
          "html": {
            "type": "string"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "TemplateSendRequest": {
        "type": "object",
        "required": [
          "from",
          "to"
        ],
        "oneOf": [
          {
            "required": [
              "templateId"
            ]
          },
          {
            "required": [
              "templateAlias"
            ]
          }
        ],
        "not": {
          "anyOf": [
            {
              "required": [
                "html"
              ]
            },
            {
              "required": [
                "text"
              ]
            }
          ]
        },
        "properties": {
          "from": {
            "$ref": "#/components/schemas/FromAddress"
          },
          "to": {
            "$ref": "#/components/schemas/ToAddresses"
          },
          "cc": {
            "$ref": "#/components/schemas/ToAddresses"
          },
          "subject": {
            "type": "string"
          },
          "templateId": {
            "type": "string"
          },
          "templateAlias": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/TemplateDataValue"
            },
            "description": "Template variables. Max 64 KiB serialized. data.email defaults to the first recipient."
          }
        }
      },
      "TemplateDataValue": {
        "description": "Strings, numbers, booleans, or nested objects of those.",
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "number"
          },
          {
            "type": "boolean"
          },
          {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/TemplateDataValue"
            }
          }
        ]
      },
      "FromAddress": {
        "type": "string",
        "format": "email",
        "description": "Bare address. Must be a verified sender identity on an allowed root."
      },
      "ToAddresses": {
        "type": "array",
        "minItems": 1,
        "items": {
          "type": "string",
          "format": "email"
        },
        "description": "Always an array. A string is a 400. Addresses are lowercased and de-duplicated; to + cc together are capped at 50."
      },
      "EmailSummary": {
        "type": "object",
        "description": "One row of the 90-day transactional send ledger.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Ledger id."
          },
          "messageId": {
            "type": "string",
            "description": "SES message id."
          },
          "rootDomain": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ]
          },
          "templateAlias": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "default": "transactional"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "queued, sent, failed, or a later delivery-event state."
          },
          "lastEventAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "htmlKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "Internal body-storage key; fetch the body via include=body instead."
          },
          "textKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "Internal body-storage key; fetch the body via include=body instead."
          },
          "inboxMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "threadId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Epoch seconds when the row leaves the 90-day ledger."
          }
        }
      },
      "EmailRecord": {
        "type": "object",
        "properties": {
          "email": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EmailSummary"
              },
              {
                "type": "object",
                "properties": {
                  "html": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Stored HTML body; null unless include=body."
                  },
                  "text": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Stored text body; null unless include=body."
                  }
                }
              }
            ]
          }
        }
      },
      "Conversation": {
        "type": "object",
        "description": "One inbox thread.",
        "properties": {
          "threadId": {
            "type": "string"
          },
          "folder": {
            "type": "string",
            "enum": [
              "INBOX",
              "SPAM",
              "SENT",
              "ARCHIVE",
              "TRASH"
            ]
          },
          "rootDomain": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "snippet": {
            "type": "string"
          },
          "lastMessageAt": {
            "type": "string"
          },
          "lastMessageId": {
            "type": "string"
          },
          "messageCount": {
            "type": "integer"
          },
          "unreadCount": {
            "type": "integer"
          },
          "participantSummary": {
            "type": "string"
          },
          "hasAttachments": {
            "type": "boolean"
          }
        }
      },
      "ConversationPage": {
        "type": "object",
        "properties": {
          "conversations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Conversation"
            }
          },
          "cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page; null when this is the last page."
          }
        }
      },
      "Message": {
        "type": "object",
        "description": "Message metadata. Bodies come from /messages/{id}/body, attachment bytes from /messages/{id}/attachments/{n}. Storage keys are never exposed.",
        "properties": {
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "IN",
              "OUT"
            ]
          },
          "folder": {
            "type": "string",
            "enum": [
              "INBOX",
              "SPAM",
              "SENT",
              "ARCHIVE",
              "TRASH"
            ]
          },
          "read": {
            "type": "boolean"
          },
          "threadId": {
            "type": "string"
          },
          "rootDomain": {
            "type": "string"
          },
          "receivedAt": {
            "type": "string"
          },
          "fromAddress": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "toAddresses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ccAddresses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "recipientAddress": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "snippet": {
            "type": "string"
          },
          "hasAttachments": {
            "type": "boolean"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttachmentInfo"
            }
          },
          "spamVerdict": {
            "type": "string"
          },
          "virusVerdict": {
            "type": "string"
          },
          "spfVerdict": {
            "type": "string"
          },
          "dkimVerdict": {
            "type": "string"
          },
          "dmarcVerdict": {
            "type": "string"
          }
        }
      },
      "AttachmentInfo": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "description": "0-based index for /messages/{id}/attachments/{n}."
          },
          "filename": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Bytes. Over 4 MiB cannot be fetched inline."
          }
        }
      },
      "InboxAddress": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "rootDomain": {
            "type": "string"
          }
        }
      },
      "SentMessageResult": {
        "type": "object",
        "title": "Sent",
        "required": [
          "id",
          "threadId"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "threadId": {
            "type": "string"
          }
        }
      },
      "DraftEnvelope": {
        "type": "object",
        "title": "Draft (nothing sent)",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "draft"
            ]
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "html": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Campaign": {
        "type": "object",
        "description": "Campaign read projection. Counters default to 0.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "SCHEDULED",
              "QUEUING",
              "SENDING",
              "SENT",
              "FAILED",
              "CANCELLED"
            ],
            "default": "DRAFT"
          },
          "rootDomain": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "listNameSnapshot": {
            "type": [
              "string",
              "null"
            ]
          },
          "senderIdentity": {
            "type": "string"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "fromName": {
            "type": [
              "string",
              "null"
            ]
          },
          "replyTo": {
            "type": [
              "string",
              "null"
            ]
          },
          "previewText": {
            "type": [
              "string",
              "null"
            ]
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ]
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "sentAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "expectedRecipientCount": {
            "type": "integer"
          },
          "recipientCount": {
            "type": "integer"
          },
          "sentCount": {
            "type": "integer"
          },
          "failedCount": {
            "type": "integer"
          },
          "deliveredCount": {
            "type": "integer"
          },
          "openCount": {
            "type": "integer"
          },
          "clickCount": {
            "type": "integer"
          },
          "bounceCount": {
            "type": "integer"
          },
          "complaintCount": {
            "type": "integer"
          },
          "unsubscribeCount": {
            "type": "integer"
          }
        }
      },
      "CreateCampaignRequest": {
        "type": "object",
        "required": [
          "name",
          "listId",
          "senderIdentity"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "listId": {
            "type": "string",
            "description": "Must belong to the sender's root."
          },
          "senderIdentity": {
            "type": "string",
            "format": "email"
          },
          "templateId": {
            "type": "string",
            "description": "A template on the sender's root, or a Shared template."
          },
          "subject": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "replyTo": {
            "type": "string"
          },
          "previewText": {
            "type": "string"
          },
          "footerText": {
            "type": "string"
          },
          "htmlBody": {
            "type": "string"
          },
          "unsubscribeFooter": {
            "type": "boolean",
            "default": true,
            "description": "false opts out of the injected unsubscribe footer."
          },
          "bodyJson": {
            "type": "string",
            "description": "Stringified editor document, as produced by the dashboard."
          },
          "scheduledAt": {
            "type": "string",
            "description": "ISO datetime."
          }
        }
      },
      "EditCampaignRequest": {
        "type": "object",
        "description": "All fields optional; omitted fields are left untouched. templateId '' and bodyJson '' clear those fields; scheduledAt null or '' clears the schedule.",
        "properties": {
          "name": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "senderIdentity": {
            "type": "string"
          },
          "templateId": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "replyTo": {
            "type": "string"
          },
          "previewText": {
            "type": "string"
          },
          "footerText": {
            "type": "string"
          },
          "htmlBody": {
            "type": "string"
          },
          "unsubscribeFooter": {
            "type": "boolean"
          },
          "bodyJson": {
            "type": "string"
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "List": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "rootDomain": {
            "type": "string"
          },
          "contactCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string"
          }
        }
      },
      "ImportJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSING",
              "COMPLETE",
              "FAILED"
            ]
          },
          "processedCount": {
            "type": "integer"
          },
          "totalRows": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          }
        }
      },
      "ContactMembership": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "unsubscribedAt": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DomainContact": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "rootDomain": {
            "type": "string"
          }
        }
      },
      "ContactProperty": {
        "type": "object",
        "description": "Identified by rootDomain + slug — there is no id.",
        "properties": {
          "slug": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "TEXT",
              "NUMBER",
              "BOOLEAN",
              "DATE"
            ]
          },
          "rootDomain": {
            "type": "string"
          }
        }
      },
      "Template": {
        "type": "object",
        "description": "Template listing shape. GET /templates/{id} adds the bodies.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "rootDomain": {
            "type": "string",
            "description": "The owning domain, or __shared__ for the Shared library."
          },
          "subject": {
            "type": "string"
          },
          "alias": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        }
      },
      "TemplateWithBody": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Template"
          },
          {
            "type": "object",
            "properties": {
              "htmlBody": {
                "type": "string"
              },
              "textBody": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        ]
      },
      "CreateTemplateRequest": {
        "type": "object",
        "required": [
          "rootDomain",
          "name",
          "subject",
          "htmlBody"
        ],
        "properties": {
          "rootDomain": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "minLength": 1
          },
          "subject": {
            "type": "string"
          },
          "htmlBody": {
            "type": "string"
          },
          "textBody": {
            "type": "string"
          },
          "bodyJson": {
            "type": "string",
            "description": "Stringified editor document."
          },
          "alias": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "description": "Unique per domain. Derived from name when omitted."
          }
        }
      },
      "EditTemplateRequest": {
        "type": "object",
        "description": "All fields optional; omitted fields are left untouched. alias '' clears the alias; bodyJson '' clears the stored document.",
        "properties": {
          "name": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "htmlBody": {
            "type": "string"
          },
          "textBody": {
            "type": "string"
          },
          "bodyJson": {
            "type": "string"
          },
          "alias": {
            "type": "string"
          }
        }
      },
      "Identity": {
        "type": "object",
        "description": "Sender identity. Tracking flags are read as clickTracking/openTracking; the PATCH request uses clickTrackingEnabled/openTrackingEnabled.",
        "properties": {
          "identity": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "DOMAIN",
              "EMAIL"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "VERIFIED",
              "FAILED"
            ]
          },
          "rootDomain": {
            "type": "string"
          },
          "openTracking": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "clickTracking": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      },
      "DnsRecord": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "CNAME",
              "TXT",
              "MX"
            ]
          },
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "purpose": {
            "type": "string"
          },
          "priority": {
            "type": "integer"
          },
          "managedBy": {
            "type": "string"
          }
        }
      },
      "SuppressionEntry": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "BOUNCE",
              "COMPLAINT",
              "MANUAL"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          }
        }
      },
      "StatsRow": {
        "type": "object",
        "description": "One day of counters for one source. Counters default to 0.",
        "properties": {
          "id": {
            "type": "string",
            "description": "root#date#source."
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD."
          },
          "source": {
            "type": "string",
            "enum": [
              "campaign",
              "transactional",
              "inbox",
              "test"
            ]
          },
          "sent": {
            "type": "integer"
          },
          "delivered": {
            "type": "integer"
          },
          "bounced": {
            "type": "integer"
          },
          "complained": {
            "type": "integer"
          },
          "opened": {
            "type": "integer"
          },
          "clicked": {
            "type": "integer"
          }
        }
      },
      "Deleted": {
        "type": "object",
        "required": [
          "deleted"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "allowedRootDomains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On domain refusals: the roots this key may use."
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On template sends: required template variables that were not supplied."
          },
          "invalid": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On template sends: data paths with invalid values."
          },
          "reason": {
            "type": "string",
            "enum": [
              "BOUNCE",
              "COMPLAINT",
              "MANUAL"
            ]
          },
          "email": {
            "type": "string"
          }
        }
      }
    }
  }
}