openapi: 3.1.0
info:
  title: 'EmailFlow AI Public API'
  version: 1.0.0
  description: |-
    The EmailFlow AI public customer API: audiences (lists, subscribers, segments), campaigns, sending domains, templates, brand profile, usage meters, the analytics event feed, AI email generation, automation graphs and runs, and account webhooks.

    Authentication: scoped API keys (`efa_…`) via `Authorization: Bearer` or `X-API-Key` (create them under Account → API). Errors use a uniform envelope (the `Error` schema). Unsafe requests accept an optional `Idempotency-Key` header (24h replay window; replays carry `Idempotency-Replayed: true`; same key + different payload → 409). Rate limits are per class (read 100/min, write 60/min, batch 10/min, ai 20/min, sends 10/min) with `X-RateLimit-*` headers on success and `Retry-After` on 429.
  contact:
    url: 'https://emailflow.ai/docs/api'
servers:
  -
    url: 'https://emailflow.ai/api/v1'
security:
  -
    bearerAuth: []
  -
    apiKeyHeader: []
tags:
  -
    name: Account
  -
    name: Lists
  -
    name: Subscribers
  -
    name: 'Bulk contacts'
  -
    name: Segments
  -
    name: Campaigns
  -
    name: Domains
  -
    name: Templates
  -
    name: Brand
  -
    name: Usage
  -
    name: Analytics
  -
    name: 'AI email generation'
  -
    name: Automations
  -
    name: Webhooks
paths:
  /analytics/events:
    get:
      operationId: listAnalyticsEvents
      summary: 'List delivery and engagement events'
      tags:
        - Analytics
      description: |-
        Required scope: `analytics` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: type
          in: query
          required: false
          description: 'Event type filter; repeatable (`type[]=open&type[]=click`).'
          schema:
            type: array
            items:
              type: string
              enum:
                - sent
                - failed
                - open
                - click
                - bounce
                - feedback
                - unsubscribe
        -
          name: campaign_uid
          in: query
          required: false
          description: 'Limit to one campaign.'
          schema:
            type: string
        -
          name: email
          in: query
          required: false
          description: 'Limit to one subscriber email.'
          schema:
            type: string
        -
          name: since
          in: query
          required: false
          description: 'ISO 8601 lower bound.'
          schema:
            type: string
            format: date-time
        -
          name: until
          in: query
          required: false
          description: 'ISO 8601 upper bound.'
          schema:
            type: string
            format: date-time
        -
          name: cursor
          in: query
          required: false
          description: 'Opaque pagination cursor from `meta.next_cursor`.'
          schema:
            type: string
        -
          name: per_page
          in: query
          required: false
          description: 'Events per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        200:
          description: 'Events newest-first with an opaque cursor.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AnalyticsEvent'
                  meta:
                    type: object
                    properties:
                      next_cursor: { type: [string, 'null'], description: 'Pass as `cursor` to fetch the next page; null when exhausted.' }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign (when filtering by campaign_uid) not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automation-runs/{run_uid}':
    patch:
      operationId: cancelAutomationRun
      summary: 'Cancel a run'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: run_uid
          in: path
          required: true
          description: 'Run uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  enum:
                    - cancel
      responses:
        200:
          description: 'Cancelled (terminal and irreversible). Cancelling an already-cancelled run is a no-op 200.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/AutomationRun'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Run not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        409:
          description: 'Run already completed/failed — terminal with a different outcome (`CONFLICT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /automations:
    get:
      operationId: listAutomations
      summary: 'List automations'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      responses:
        200:
          description: 'Automations (legacy-pinned top-level array of `{uid, name, status, created_at, updated_at}`).'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createAutomation
      summary: 'Create an automation (graph or AI prompt)'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - list_uid
              description: 'Provide EITHER a validated graph (`trigger` + `nodes`) OR an AI composition `prompt` — exactly one. The prompt branch also consumes the `api.ai` limiter and AI token credits.'
              properties:
                name:
                  type: string
                  maxLength: 255
                list_uid:
                  type: string
                trigger:
                  $ref: '#/components/schemas/AutomationTrigger'
                nodes:
                  type: array
                  items:
                    $ref: '#/components/schemas/AutomationNode'
                prompt:
                  type: string
                  maxLength: 4000
      responses:
        201:
          description: 'Created (inactive). The prompt branch adds `tokens_used`.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Automation'
                  tokens_used:
                    type: integer
                    description: 'Present only when composed from a prompt.'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Automation quota reached (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        502:
          description: 'AI provider failure on the prompt branch (`SERVER_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        504:
          description: 'AI composition timed out on the prompt branch (`SERVER_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automations/{automation_uid}':
    get:
      operationId: getAutomation
      summary: 'Get an automation with its graph'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      responses:
        200:
          description: 'The automation, canonical graph, and validator issues.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Automation'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateAutomation
      summary: 'Apply an atomic batch of graph operations'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - operations
              description: 'All-or-nothing: the first invalid operation aborts the whole batch (422 with the failing index in `details`). `add_node` supports `as` aliases referenced by later operations (`$alias`, `$prev`).'
              properties:
                operations:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required:
                      - op
                    properties:
                      op: { type: string, enum: [add_node, update_node, delete_node, update_trigger] }
                      type: { type: string }
                      parent_id: { type: string }
                      branch: { type: [string, 'null'] }
                      node_id: { type: string }
                      key: { type: string }
                      mode: { type: string, enum: [shift, cascade], default: shift }
                      options: { type: object, additionalProperties: true }
                      as: { type: string }
      responses:
        200:
          description: 'All operations applied; the stored graph was replaced atomically.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - results
                properties:
                  data:
                    $ref: '#/components/schemas/Automation'
                  results:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteAutomation
      summary: 'Delete an automation'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
        -
          name: force
          in: query
          required: false
          description: 'Set to 1 to delete an active automation.'
          schema:
            type: integer
            enum:
              - 0
              - 1
            default: 0
      responses:
        200:
          description: 'Deleted with its runs and email content.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      deleted: { type: boolean }
                      uid: { type: string }
                      runs_deleted: { type: integer }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        409:
          description: 'Automation is active; deactivate first or pass `?force=1` (`CONFLICT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automations/{automation_uid}/activate':
    post:
      operationId: activateAutomation
      summary: 'Activate an automation (idempotent)'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      responses:
        200:
          description: Active.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      uid: { type: string }
                      status: { type: string }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automations/{automation_uid}/deactivate':
    post:
      operationId: deactivateAutomation
      summary: 'Deactivate an automation (idempotent)'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Inactive. In-flight runs pause; they resume if reactivated.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      uid: { type: string }
                      status: { type: string }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automations/{automation_uid}/execute':
    post:
      operationId: executeAutomation
      summary: 'Trigger an automation for its whole list (deprecated)'
      tags:
        - Automations
      description: |-
        Deprecated legacy alias. Use `POST /automations/{automation_uid}/runs` to fire per contact.

        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      deprecated: true
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Legacy-pinned `{success: true}`.'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/automations/{automation_uid}/runs':
    get:
      operationId: listAutomationRuns
      summary: 'List runs of an automation'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
        -
          name: status
          in: query
          required: false
          description: 'Filter by run phase.'
          schema:
            type: string
            enum:
              - running
              - waiting
              - completed
              - failed
              - cancelled
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Runs per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated runs, newest first.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AutomationRun'
                  meta:
                    type: object
                    properties:
                      total: { type: integer }
                      per_page: { type: integer }
                      current_page: { type: integer }
                      last_page: { type: integer }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Automation not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createAutomationRun
      summary: 'Fire an automation for one contact'
      tags:
        - Automations
      description: |-
        Required scope: `automations` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: automation_uid
          in: path
          required: true
          description: 'Automation uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Identify the contact by `email` or `subscriber_uid` (exactly one required). `payload` is stored on the run state for audit.'
              properties:
                email:
                  type: string
                  format: email
                subscriber_uid:
                  type: string
                payload:
                  type: object
                  additionalProperties: true
      responses:
        200:
          description: 'The contact was already enrolled — the existing run is returned (idempotent per contact).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/AutomationRun'
        201:
          description: 'Run created.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/AutomationRun'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: "Automation or contact (contact must be on the automation's list) not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        409:
          description: 'Automation inactive, contact not subscribed, or entry conditions not met (`CONFLICT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /brand:
    get:
      operationId: getBrand
      summary: 'Get the account brand profile'
      tags:
        - Brand
      description: |-
        Required scope: `brand` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      responses:
        200:
          description: 'The brand profile.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/BrandProfile'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'No brand profile exists yet (`NOT_FOUND`; suggestion points to running a brand scan).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /campaigns:
    get:
      operationId: listCampaigns
      summary: 'List campaigns'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Campaigns (legacy-pinned top-level array).'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CampaignSummary'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createCampaign
      summary: 'Create a campaign from raw HTML'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - subject
                - from_email
                - from_name
                - reply_to
                - html
              properties:
                name:
                  type: string
                subject:
                  type: string
                from_email:
                  type: string
                  format: email
                from_name:
                  type: string
                reply_to:
                  type: string
                  format: email
                html:
                  type: string
                  description: 'Full email HTML body.'
                list_uid:
                  type: string
              additionalProperties: true
      responses:
        200:
          description: 'Created (legacy-pinned `{status:1, message, attributes}`).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}':
    get:
      operationId: getCampaign
      summary: 'Get a campaign with statistics'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Campaign and statistics.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignDetail'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateCampaign
      summary: 'Update a campaign'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        200:
          description: 'Updated (legacy-pinned `{status:1, message, attributes}`).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      operationId: replaceCampaign
      summary: 'Update a campaign (PUT alias)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        200:
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteCampaign
      summary: 'Delete a campaign'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: Deleted.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/bounce-log/download':
    get:
      operationId: downloadCampaignBounceLog
      summary: 'Download campaign bounce log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the bounce log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/click-log/download':
    get:
      operationId: downloadCampaignClickLog
      summary: 'Download campaign click log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the click log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/feedback-log/download':
    get:
      operationId: downloadCampaignFeedbackLog
      summary: 'Download campaign feedback log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the feedback log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/open-log/download':
    get:
      operationId: downloadCampaignOpenLog
      summary: 'Download campaign open log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the open log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/pause':
    post:
      operationId: pauseCampaign
      summary: 'Pause a sending campaign'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: Paused.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/resume':
    post:
      operationId: resumeCampaign
      summary: 'Resume a paused campaign'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: Resumed.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/run':
    post:
      operationId: runCampaign
      summary: 'Queue a campaign for sending'
      tags:
        - Campaigns
      description: |-
        Required scope: `sends` (the `all` scope always satisfies).

        Rate limit class: `api.sends`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Queued for delivery.'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/tracking-log/download':
    get:
      operationId: downloadCampaignTrackingLog
      summary: 'Download campaign tracking log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the tracking log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/campaigns/{campaign_uid}/unsubscribe-log/download':
    get:
      operationId: downloadCampaignUnsubscribeLog
      summary: 'Download campaign unsubscribe log (CSV)'
      tags:
        - Campaigns
      description: |-
        Required scope: `campaigns` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: campaign_uid
          in: path
          required: true
          description: 'Campaign uid.'
          schema:
            type: string
      responses:
        200:
          description: 'CSV export of the unsubscribe log.'
          content:
            text/csv:
              schema:
                type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Campaign not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /domains:
    get:
      operationId: listDomains
      summary: 'List sending domains'
      tags:
        - Domains
      description: |-
        Required scope: `domains` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated domains with per-record DNS status.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Domain'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createDomain
      summary: 'Register a sending domain'
      tags:
        - Domains
      description: |-
        Required scope: `domains` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - domain
              properties:
                domain:
                  type: string
                  description: 'Domain name. Internationalized names are accepted and normalized to punycode.'
      responses:
        201:
          description: 'Registered; publish the returned `records` then call verify.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Domain'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Sending-domain quota reached (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/domains/{domain_uid}':
    delete:
      operationId: deleteDomain
      summary: 'Delete a sending domain'
      tags:
        - Domains
      description: |-
        Required scope: `domains` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: domain_uid
          in: path
          required: true
          description: 'Domain uid.'
          schema:
            type: string
      responses:
        200:
          description: Deleted.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      deleted: { type: boolean }
                      uid: { type: string }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Domain not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        409:
          description: 'Domain is in use by scheduled or sending campaigns (`CONFLICT`; blocking campaigns listed in `details.campaigns`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/domains/{domain_uid}/verify':
    post:
      operationId: verifyDomain
      summary: 'Run DNS verification for a domain'
      tags:
        - Domains
      description: |-
        Required scope: `domains` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: domain_uid
          in: path
          required: true
          description: 'Domain uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Fresh verification state after checking DNS.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Domain'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Domain not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /emails/generate:
    post:
      operationId: generateEmail
      summary: 'Generate an email template from a prompt'
      tags:
        - 'AI email generation'
      description: |-
        Synchronous generation (typically 15-60s). Send an `Idempotency-Key` header — a replayed key returns the same `template_uid` instead of generating twice.

        Required scope: `emails` (the `all` scope always satisfies).

        Rate limit class: `api.ai`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - prompt
              properties:
                prompt:
                  type: string
                  maxLength: 4000
                name:
                  type:
                    - string
                    - 'null'
                  maxLength: 255
                apply_brand:
                  type: boolean
                  default: true
                  description: 'Inject the account brand profile into the generation context.'
      responses:
        201:
          description: 'Generated. The template is saved and openable in the Studio builder.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - template_uid
                  - name
                  - html
                  - tokens_used
                properties:
                  template_uid:
                    type: string
                  name:
                    type: string
                  html:
                    type: string
                  tokens_used:
                    type: integer
                  subject_suggestion:
                    type: string
                    description: 'Present when the generated document has a usable <title>.'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'AI token credits exhausted (`CREDITS_EXHAUSTED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        500:
          description: 'The model returned no HTML artifact (`SERVER_ERROR`, reason `no_html_artifact`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        502:
          description: 'Upstream AI provider error (`SERVER_ERROR`, reason `provider_error`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        504:
          description: 'Generation exceeded the 120s budget (`SERVER_ERROR`, reason `generation_timeout`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/emails/{template_uid}':
    patch:
      operationId: editEmail
      summary: 'Edit a template with a natural-language instruction'
      tags:
        - 'AI email generation'
      description: |-
        Required scope: `emails` (the `all` scope always satisfies).

        Rate limit class: `api.ai`.
      parameters:
        -
          name: template_uid
          in: path
          required: true
          description: 'Template uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - instruction
              properties:
                instruction:
                  type: string
                  maxLength: 2000
      responses:
        200:
          description: "Edited. A checkpoint is recorded in the template's Studio chat history."
          content:
            application/json:
              schema:
                type: object
                required:
                  - template_uid
                  - html
                  - tokens_used
                  - checkpoint_id
                properties:
                  template_uid:
                    type: string
                  html:
                    type: string
                  tokens_used:
                    type: integer
                  checkpoint_id:
                    type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'AI token credits exhausted (`CREDITS_EXHAUSTED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Template not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        500:
          description: 'The model returned no HTML artifact (`SERVER_ERROR`, reason `no_html_artifact`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        502:
          description: 'Upstream AI provider error (`SERVER_ERROR`, reason `provider_error`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        504:
          description: 'Generation exceeded the 120s budget (`SERVER_ERROR`, reason `generation_timeout`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/import-jobs/{job_uid}':
    get:
      operationId: getImportJob
      summary: 'Poll an import job'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: job_uid
          in: path
          required: true
          description: 'Import job uid (from the async submit response).'
          schema:
            type: string
      responses:
        200:
          description: 'Current job status and progress.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportJob'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Import job not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: cancelImportJob
      summary: 'Cancel an import job'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.batch`.
      parameters:
        -
          name: job_uid
          in: path
          required: true
          description: 'Import job uid (from the async submit response).'
          schema:
            type: string
      responses:
        200:
          description: Cancelled.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  job_uid:
                    type: string
                  status:
                    type: string
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Import job not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /lists:
    get:
      operationId: listLists
      summary: 'List mail lists'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'All mail lists owned by the account.'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/List'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createList
      summary: 'Create a mail list'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - from_email
                - from_name
              properties:
                name:
                  type: string
                from_email:
                  type: string
                  format: email
                from_name:
                  type: string
                default_subject:
                  type: string
                contact:
                  type: object
                  description: 'Company contact block (company, address_1, city, country_id, …).'
                  additionalProperties: true
                subscribe_confirmation:
                  type: boolean
                send_welcome_email:
                  type: boolean
                unsubscribe_notification:
                  type: boolean
              additionalProperties: true
      responses:
        200:
          description: 'Created (legacy-pinned `{status, list_uid, message}`).'
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                  list_uid:
                    type: string
                  message:
                    type: string
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Plan list quota reached (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}':
    get:
      operationId: getList
      summary: 'Get a mail list'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      responses:
        200:
          description: 'The list, including custom field definitions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/List'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateList
      summary: 'Update a mail list'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - from_email
                - from_name
              properties:
                name:
                  type: string
                from_email:
                  type: string
                  format: email
                from_name:
                  type: string
                default_subject:
                  type: string
                contact:
                  type: object
                  description: 'Company contact block (company, address_1, city, country_id, …).'
                  additionalProperties: true
                subscribe_confirmation:
                  type: boolean
                send_welcome_email:
                  type: boolean
                unsubscribe_notification:
                  type: boolean
              additionalProperties: true
      responses:
        200:
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      operationId: replaceList
      summary: 'Update a mail list (PUT alias)'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - from_email
                - from_name
              properties:
                name:
                  type: string
                from_email:
                  type: string
                  format: email
                from_name:
                  type: string
                default_subject:
                  type: string
                contact:
                  type: object
                  description: 'Company contact block (company, address_1, city, country_id, …).'
                  additionalProperties: true
                subscribe_confirmation:
                  type: boolean
                send_welcome_email:
                  type: boolean
                unsubscribe_notification:
                  type: boolean
              additionalProperties: true
      responses:
        200:
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteList
      summary: 'Delete a mail list'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Deleted (subscribers and segments cascade).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/add-field':
    post:
      operationId: addListField
      summary: 'Add a custom field to a list'
      tags:
        - Lists
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - type
                - label
                - tag
              properties:
                type:
                  type: string
                  enum:
                    - text
                    - number
                    - datetime
                    - date
                label:
                  type: string
                tag:
                  type: string
                  description: 'Merge-tag name, e.g. CITY.'
                default_value:
                  type: string
      responses:
        200:
          description: 'Field created; response echoes the field definition.'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/import-jobs':
    get:
      operationId: listImportJobs
      summary: 'List import jobs for a list'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Recent import jobs.'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ImportJob'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/segments':
    get:
      operationId: listSegments
      summary: 'List segments of a list'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated segments.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Segment'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createSegment
      summary: 'Create a segment'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - matching
                - conditions
              properties:
                name:
                  type: string
                matching:
                  type: string
                  enum:
                    - all
                    - any
                conditions:
                  type: array
                  minItems: 1
                  items:
                    $ref: '#/components/schemas/SegmentCondition'
      responses:
        201:
          description: Created.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Segment'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Per-list segment quota reached (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/subscribers/bulk':
    post:
      operationId: bulkUpsertSubscribers
      summary: 'Bulk create/update subscribers (sync)'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.batch`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - subscribers
              properties:
                subscribers:
                  type: array
                  maxItems: 1000
                  description: 'Up to 1,000 rows per request. Each row: EMAIL plus custom-field tag keys.'
                  items:
                    type: object
                    required:
                      - EMAIL
                    properties:
                      EMAIL: { type: string, format: email }
                      tag: { type: string }
                    additionalProperties:
                      type: string
      responses:
        200:
          description: 'Per-row outcome.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkUpsertResult'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Plan subscriber quota would be exceeded (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        413:
          description: 'More than 1,000 rows (`VALIDATION_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: bulkDeleteSubscribers
      summary: 'Bulk delete subscribers by email (sync)'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.batch`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - emails
              properties:
                emails:
                  type: array
                  maxItems: 1000
                  items:
                    type: string
                    format: email
      responses:
        200:
          description: 'Per-row outcome.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - processed
                  - deleted
                  - failed
                  - errors
                  - warnings
                properties:
                  processed:
                    type: integer
                  deleted:
                    type: integer
                  failed:
                    type: integer
                  errors:
                    type: array
                    items:
                      $ref: '#/components/schemas/BulkRowIssue'
                  warnings:
                    type: array
                    items:
                      $ref: '#/components/schemas/BulkRowIssue'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        413:
          description: 'More than 1,000 rows (`VALIDATION_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/subscribers/bulk/async':
    post:
      operationId: bulkUpsertSubscribersAsync
      summary: 'Bulk create/update subscribers (async import job)'
      tags:
        - 'Bulk contacts'
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.batch`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - subscribers
              properties:
                subscribers:
                  type: array
                  maxItems: 1000
                  description: 'Up to 1,000 rows per request. Each row: EMAIL plus custom-field tag keys.'
                  items:
                    type: object
                    required:
                      - EMAIL
                    properties:
                      EMAIL: { type: string, format: email }
                      tag: { type: string }
                    additionalProperties:
                      type: string
      responses:
        202:
          description: 'Job accepted.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - job_uid
                  - status
                  - status_url
                properties:
                  job_uid:
                    type: string
                  status:
                    type: string
                  status_url:
                    type: string
                    format: uri
                  cancel_url:
                    type: string
                    format: uri
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'List not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        413:
          description: 'More than 1,000 rows (`VALIDATION_ERROR`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/subscribers/email/{email}/unsubscribe':
    patch:
      operationId: unsubscribeSubscriberByEmail
      summary: 'Unsubscribe by email address'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
        -
          name: email
          in: path
          required: true
          description: 'Subscriber email address.'
          schema:
            type: string
      responses:
        200:
          description: Unsubscribed.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber or list not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/subscribers/{subscriber_uid}/subscribe':
    patch:
      operationId: subscribeSubscriber
      summary: 'Mark a subscriber subscribed'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      responses:
        200:
          description: 'Subscribed (legacy-pinned `{status, message}`).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber or list not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/lists/{list_uid}/subscribers/{subscriber_uid}/unsubscribe':
    patch:
      operationId: unsubscribeSubscriber
      summary: 'Mark a subscriber unsubscribed'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: list_uid
          in: path
          required: true
          description: 'List uid.'
          schema:
            type: string
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      responses:
        200:
          description: 'Unsubscribed (legacy-pinned `{status, message}`).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber or list not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /me:
    get:
      operationId: getMe
      summary: 'Get the authenticated user'
      tags:
        - Account
      description: |-
        Works with any valid credential regardless of scopes — useful as an auth smoke test.

        Rate limit class: `api.read`.
      responses:
        200:
          description: 'The user record the presented credential resolves to. The legacy `api_token` is never serialized.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  uid:
                    type: string
                  email:
                    type: string
                    format: email
                  first_name:
                    type:
                      - string
                      - 'null'
                  last_name:
                    type:
                      - string
                      - 'null'
                  customer_id:
                    type:
                      - integer
                      - 'null'
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/segments/{segment_uid}':
    get:
      operationId: getSegment
      summary: 'Get a segment'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: segment_uid
          in: path
          required: true
          description: 'Segment uid.'
          schema:
            type: string
      responses:
        200:
          description: 'The segment with a live subscriber count.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Segment'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Segment not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateSegment
      summary: 'Update a segment'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: segment_uid
          in: path
          required: true
          description: 'Segment uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                matching:
                  type: string
                  enum:
                    - all
                    - any
                conditions:
                  type: array
                  minItems: 1
                  items:
                    $ref: '#/components/schemas/SegmentCondition'
      responses:
        200:
          description: 'Updated. Sending `conditions` replaces the whole set.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Segment'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Segment not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteSegment
      summary: 'Delete a segment'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: segment_uid
          in: path
          required: true
          description: 'Segment uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Deleted. Historical campaign statistics are unaffected.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      deleted: { type: boolean }
                      uid: { type: string }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Segment not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/segments/{segment_uid}/subscribers':
    get:
      operationId: listSegmentSubscribers
      summary: 'List subscribers matching a segment'
      tags:
        - Segments
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: segment_uid
          in: path
          required: true
          description: 'Segment uid.'
          schema:
            type: string
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated matching subscribers (same row shape as the list-subscribers endpoint).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Subscriber'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Segment not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /subscribers:
    get:
      operationId: listSubscribers
      summary: 'List subscribers'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: list_uid
          in: query
          required: false
          description: 'Limit to one list.'
          schema:
            type: string
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Subscribers (filter with `list_uid`; paginated).'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Subscriber'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createSubscriber
      summary: 'Create or update a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - EMAIL
                - list_uid
              properties:
                EMAIL:
                  type: string
                  format: email
                list_uid:
                  type: string
                tag:
                  type: string
                  description: 'Comma-separated tags.'
                status:
                  type: string
                  enum:
                    - subscribed
                    - unsubscribed
                    - unconfirmed
              additionalProperties:
                type: string
              description: 'Custom-field values are passed as additional UPPERCASE tag keys (e.g. FIRST_NAME).'
      responses:
        200:
          description: 'Created/updated (legacy-pinned `{status, subscriber_uid, message}`).'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        402:
          description: 'Plan subscriber quota reached (`PLAN_LIMIT`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/subscribers/email/{email}':
    get:
      operationId: getSubscriberByEmail
      summary: 'Find subscribers by email'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: email
          in: path
          required: true
          description: 'Subscriber email address.'
          schema:
            type: string
      responses:
        200:
          description: "Matching subscribers across the account's lists."
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Subscriber'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/subscribers/{subscriber_uid}':
    get:
      operationId: getSubscriber
      summary: 'Get a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      responses:
        200:
          description: 'The subscriber.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Subscriber'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateSubscriber
      summary: 'Update a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - EMAIL
                - list_uid
              properties:
                EMAIL:
                  type: string
                  format: email
                list_uid:
                  type: string
                tag:
                  type: string
                  description: 'Comma-separated tags.'
                status:
                  type: string
                  enum:
                    - subscribed
                    - unsubscribed
                    - unconfirmed
              additionalProperties:
                type: string
              description: 'Custom-field values are passed as additional UPPERCASE tag keys (e.g. FIRST_NAME).'
      responses:
        200:
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      operationId: replaceSubscriber
      summary: 'Update a subscriber (PUT alias)'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - EMAIL
                - list_uid
              properties:
                EMAIL:
                  type: string
                  format: email
                list_uid:
                  type: string
                tag:
                  type: string
                  description: 'Comma-separated tags.'
                status:
                  type: string
                  enum:
                    - subscribed
                    - unsubscribed
                    - unconfirmed
              additionalProperties:
                type: string
              description: 'Custom-field values are passed as additional UPPERCASE tag keys (e.g. FIRST_NAME).'
      responses:
        200:
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteSubscriber
      summary: 'Delete a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      responses:
        200:
          description: Deleted.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/subscribers/{subscriber_uid}/add-tag':
    post:
      operationId: addSubscriberTag
      summary: 'Add tags to a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tag
              properties:
                tag:
                  type: string
                  description: 'Comma-separated tags.'
      responses:
        200:
          description: 'Tags added.'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/subscribers/{subscriber_uid}/remove-tag':
    post:
      operationId: removeSubscriberTag
      summary: 'Remove tags from a subscriber'
      tags:
        - Subscribers
      description: |-
        Required scope: `contacts` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: subscriber_uid
          in: path
          required: true
          description: 'Subscriber uid (numeric id also accepted on legacy routes).'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tag
              properties:
                tag:
                  type: string
      responses:
        200:
          description: 'Tags removed.'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Subscriber not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /templates:
    get:
      operationId: listTemplates
      summary: 'List email templates'
      tags:
        - Templates
      description: |-
        Required scope: `templates` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: search
          in: query
          required: false
          description: 'Filter by name substring.'
          schema:
            type: string
        -
          name: source
          in: query
          required: false
          description: 'Filter by provenance.'
          schema:
            type: string
            enum:
              - gallery
              - ai
              - imported
              - api
        -
          name: page
          in: query
          required: false
          description: 'Page number (1-based).'
          schema:
            type: integer
            minimum: 1
            default: 1
        -
          name: per_page
          in: query
          required: false
          description: 'Items per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated templates, newest-updated first.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Template'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/templates/{template_uid}':
    get:
      operationId: getTemplate
      summary: 'Get a template'
      tags:
        - Templates
      description: |-
        Required scope: `templates` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: template_uid
          in: path
          required: true
          description: 'Template uid.'
          schema:
            type: string
        -
          name: include
          in: query
          required: false
          description: 'Set to `html` to embed the template HTML.'
          schema:
            type: string
            enum:
              - html
      responses:
        200:
          description: 'The template (add `?include=html` for the full HTML body).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Template'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Template not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /usage:
    get:
      operationId: getUsage
      summary: 'Get consolidated account usage'
      tags:
        - Usage
      description: |-
        Required scope: `usage` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      responses:
        200:
          description: 'Plan, quota meters, and credit meters.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Usage'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /webhooks:
    get:
      operationId: listWebhooks
      summary: 'List webhook subscriptions'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      responses:
        200:
          description: 'All subscriptions, newest first (secrets never serialize).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscription'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createWebhook
      summary: 'Create a webhook subscription'
      tags:
        - Webhooks
      description: |-
        Every delivery is signed with `X-EmailFlow-Signature: t=<unix>,v1=<hmac_sha256(secret, t + "." + body)>`. Real events queue with [30s, 5m, 30m] retries; 20 consecutive failures auto-disable the endpoint.

        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
                - events
              properties:
                url:
                  type: string
                  format: uri
                  maxLength: 2048
                  description: 'Must be https (plain http allowed only for localhost receivers).'
                events:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum:
                      - email.delivered
                      - email.failed
                      - email.opened
                      - email.clicked
                      - email.bounced
                      - email.complained
                      - contact.unsubscribed
                      - contact.subscribed
      responses:
        201:
          description: 'Created. `secret` is returned ONLY here — store it now; rotate by delete + recreate.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    allOf:
                      - { $ref: '#/components/schemas/WebhookSubscription' }
                      - { type: object, required: [secret], properties: { secret: { type: string, description: 'HMAC signing secret (shown once).' } } }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/webhooks/{webhook_uid}':
    get:
      operationId: getWebhook
      summary: 'Get a webhook subscription'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: webhook_uid
          in: path
          required: true
          description: 'Webhook subscription uid.'
          schema:
            type: string
      responses:
        200:
          description: 'The subscription.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/WebhookSubscription'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Webhook subscription not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateWebhook
      summary: 'Update a webhook subscription'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: webhook_uid
          in: path
          required: true
          description: 'Webhook subscription uid.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                  maxLength: 2048
                events:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum:
                      - email.delivered
                      - email.failed
                      - email.opened
                      - email.clicked
                      - email.bounced
                      - email.complained
                      - contact.unsubscribed
                      - contact.subscribed
                active:
                  type: boolean
      responses:
        200:
          description: 'Updated. Setting `active: true` on an auto-disabled endpoint re-enables it and clears the strike counter.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/WebhookSubscription'
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Webhook subscription not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteWebhook
      summary: 'Delete a webhook subscription'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: webhook_uid
          in: path
          required: true
          description: 'Webhook subscription uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Deleted (delivery history cascades).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      deleted: { type: boolean }
                      uid: { type: string }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Webhook subscription not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/webhooks/{webhook_uid}/deliveries':
    get:
      operationId: listWebhookDeliveries
      summary: 'List recent deliveries of a webhook'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.read`.
      parameters:
        -
          name: webhook_uid
          in: path
          required: true
          description: 'Webhook subscription uid.'
          schema:
            type: string
        -
          name: status
          in: query
          required: false
          description: 'Filter by delivery status.'
          schema:
            type: string
            enum:
              - pending
              - success
              - failed
        -
          name: per_page
          in: query
          required: false
          description: 'Deliveries per page.'
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
      responses:
        200:
          description: 'Paginated deliveries, newest first (raw rows purge after 30 days).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookDelivery'
                  meta:
                    type: object
                    properties:
                      total: { type: integer }
                      per_page: { type: integer }
                      current_page: { type: integer }
                      last_page: { type: integer }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Webhook subscription not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/webhooks/{webhook_uid}/test':
    post:
      operationId: testWebhook
      summary: 'Send a synchronous signed ping'
      tags:
        - Webhooks
      description: |-
        Required scope: `webhooks` (the `all` scope always satisfies).

        Rate limit class: `api.write`.
      parameters:
        -
          name: webhook_uid
          in: path
          required: true
          description: 'Webhook subscription uid.'
          schema:
            type: string
      responses:
        200:
          description: 'Live receiver result (single attempt, no retries — unlike real events).'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      delivery_uid: { type: string }
                      event: { type: string }
                      status: { type: string, enum: [success, failed] }
                      response_code: { type: [integer, 'null'] }
                      duration_ms: { type: [integer, 'null'] }
                      error: { type: [string, 'null'] }
                      signature_header: { type: string, description: 'The header name carrying the HMAC signature.' }
        401:
          description: 'Missing or invalid credentials (`INVALID_API_KEY` / `UNAUTHENTICATED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        403:
          description: 'The API key lacks the required scope (`INSUFFICIENT_PERMISSIONS`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: 'Webhook subscription not found (`NOT_FOUND`). Cross-tenant uids return this identical envelope — existence is never leaked.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        422:
          description: 'Validation failed (`VALIDATION_ERROR`; field messages in `details`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        429:
          description: 'Rate limited (`RATE_LIMITED`; see `Retry-After` and `retryAfter`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Scoped API key (`efa_…`) in the Authorization header: `Authorization: Bearer efa_…`. Legacy account tokens are also accepted here and via `?api_token=` (full access, no scopes — prefer scoped keys).'
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'Scoped API key (`efa_…`) in the `X-API-Key` header — equivalent to bearerAuth.'
  schemas:
    AnalyticsEvent:
      type: object
      properties:
        type:
          type: string
          enum:
            - sent
            - failed
            - open
            - click
            - bounce
            - feedback
            - unsubscribe
        occurred_at:
          type: string
          format: date-time
        campaign_uid:
          type:
            - string
            - 'null'
        campaign_name:
          type:
            - string
            - 'null'
        subscriber_email:
          type:
            - string
            - 'null'
        meta:
          type: object
          description: 'Type-specific detail (e.g. url for clicks, bounce_type for bounces, ip_address/user_agent when recorded).'
          additionalProperties: true
    Automation:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        status:
          type: string
          enum:
            - active
            - inactive
        list_uid:
          type:
            - string
            - 'null'
        graph:
          $ref: '#/components/schemas/AutomationGraph'
        issues:
          type: array
          description: 'Per-node readiness findings (same validator the flow editor renders). Empty when the automation is fully configured.'
          items:
            type: object
            properties:
              node_id:
                type: string
              message:
                type: string
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    AutomationGraph:
      type: object
      required:
        - trigger
        - nodes
      properties:
        trigger:
          $ref: '#/components/schemas/AutomationTrigger'
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/AutomationNode'
    AutomationNode:
      type: object
      required:
        - id
        - type
        - parent_id
      description: 'One graph node. `parent_id` points at the preceding node (the trigger node for roots); `branch` is required on children of condition nodes.'
      properties:
        id:
          type: string
        type:
          type: string
          enum:
            - send
            - wait
            - wait_until
            - condition
            - operation
            - webhook
        parent_id:
          type: string
        branch:
          type:
            - string
            - 'null'
          enum:
            - 'yes'
            - 'no'
            - null
        options:
          type: object
          additionalProperties: true
    AutomationRun:
      type: object
      description: "One contact's journey through one automation."
      properties:
        uid:
          type: string
        subscriber_uid:
          type:
            - string
            - 'null'
        email:
          type:
            - string
            - 'null'
        status:
          type: string
          enum:
            - running
            - waiting
            - completed
            - failed
            - cancelled
        current_node_id:
          type:
            - string
            - 'null'
        scheduled_at:
          type:
            - string
            - 'null'
          format: date-time
        error:
          type:
            - string
            - 'null'
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    AutomationTrigger:
      type: object
      required:
        - key
      properties:
        key:
          type: string
          enum:
            - welcome-new-subscriber
            - say-goodbye-subscriber
            - say-happy-birthday
            - subscriber-added-date
            - specific-date
            - api-3-0
            - weekly-recurring
            - monthly-recurring
            - tag-based
            - remove-tag
            - attribute-update
            - woo-abandoned-cart
        options:
          type: object
          additionalProperties: true
    BrandProfile:
      type: object
      properties:
        uid:
          type: string
        company_name:
          type:
            - string
            - 'null'
        website:
          type:
            - string
            - 'null'
        tagline:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        colors:
          type: array
          items:
            type: object
            additionalProperties: true
        fonts:
          type: array
          items:
            type: object
            additionalProperties: true
        personality:
          type: array
          items:
            type: string
        social_links:
          type: array
          items:
            type: object
            additionalProperties: true
        logo_url:
          type:
            - string
            - 'null'
          format: uri
        source:
          type:
            - string
            - 'null'
        crawl_status:
          type:
            - string
            - 'null'
          description: 'Brand-scan progress: idle, scraping, analyzing, importing_logo, extracting_colors, completed, failed. Poll until completed.'
        crawl_error:
          type:
            - string
            - 'null'
        last_crawled_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    BulkRowIssue:
      type: object
      properties:
        index:
          type: integer
          description: '0-based input row index.'
        email:
          type:
            - string
            - 'null'
        code:
          type: string
        message:
          type: string
    BulkUpsertResult:
      type: object
      required:
        - processed
        - created
        - updated
        - failed
        - errors
        - warnings
      properties:
        processed:
          type: integer
        created:
          type: integer
        updated:
          type: integer
        failed:
          type: integer
        errors:
          type: array
          items:
            $ref: '#/components/schemas/BulkRowIssue'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/BulkRowIssue'
    CampaignDetail:
      type: object
      required:
        - campaign
        - statistics
      properties:
        campaign:
          type: object
          properties:
            uid:
              type: string
            name:
              type: string
            list:
              type: string
            segment:
              type: string
            from_email:
              type:
                - string
                - 'null'
            from_name:
              type:
                - string
                - 'null'
            status:
              type: string
            created_at:
              type: string
            updated_at:
              type: string
          additionalProperties: true
        statistics:
          type: object
          description: 'Aggregate delivery/engagement statistics (open/click/bounce/unsubscribe rates and counts, top links/locations/subscribers).'
          additionalProperties: true
    CampaignSummary:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        type:
          type: string
        subject:
          type:
            - string
            - 'null'
        from_email:
          type:
            - string
            - 'null'
        from_name:
          type:
            - string
            - 'null'
        reply_to:
          type:
            - string
            - 'null'
        status:
          type: string
          enum:
            - new
            - queuing
            - queued
            - ready
            - sending
            - error
            - done
            - paused
            - scheduled
        subscribers:
          type: integer
        sent_percent:
          type: number
        sent_label:
          type: string
        open_rate:
          type: number
        click_rate:
          type: number
        delivery_at:
          type:
            - string
            - 'null'
        created_at:
          type: string
        updated_at:
          type: string
      additionalProperties: true
    CreditMeter:
      type: object
      description: 'A consumable credit from the subscription snapshot. Keys absent from the response were never granted to this subscription.'
      properties:
        granted:
          type:
            - integer
            - 'null'
        used:
          type:
            - integer
            - 'null'
        remaining:
          type:
            - integer
            - 'null'
        unlimited:
          type: boolean
    DnsRecord:
      type: object
      properties:
        purpose:
          type: string
          description: 'What the record is for (e.g. dkim, spf, tracking_cname, verification).'
        type:
          type: string
          description: 'DNS record type. LOCAL_DKIM mode emits TXT DKIM records; vendor mode emits CNAMEs.'
        host:
          type: string
        value:
          type: string
        status:
          type: string
        verified:
          type: boolean
    Domain:
      type: object
      properties:
        uid:
          type: string
        domain:
          type: string
          description: 'Punycode-normalized lowercase domain.'
        status:
          type: string
        management_mode:
          type:
            - string
            - 'null'
          description: 'VENDOR_SYNC (provider-managed) or LOCAL_DKIM (self-hosted signing).'
        verified:
          type: boolean
        signing_enabled:
          type: boolean
        records:
          type: array
          items:
            $ref: '#/components/schemas/DnsRecord'
        created_at:
          type:
            - string
            - 'null'
          format: date-time
    Error:
      type: object
      description: 'Canonical error envelope. Scoped (`efa_`) callers receive this at the top level for every error status; legacy-token callers keep their pre-2026 response bodies with this envelope added under an `error` key.'
      required:
        - code
        - type
        - message
      properties:
        code:
          type: string
          description: 'Stable machine code, e.g. VALIDATION_ERROR, NOT_FOUND, RATE_LIMITED, PLAN_LIMIT, CREDITS_EXHAUSTED.'
        type:
          type: string
          description: 'Error class, e.g. invalid_request_error, authentication_error, rate_limit_error.'
        message:
          type: string
        suggestion:
          type:
            - string
            - 'null'
          description: 'Actionable next step.'
        docs:
          type:
            - string
            - 'null'
          format: uri
        param:
          type:
            - string
            - 'null'
          description: 'Offending parameter, when attributable.'
        details:
          type:
            - object
            - 'null'
          description: 'Field-keyed validation messages.'
          additionalProperties: true
        retryAfter:
          type:
            - integer
            - 'null'
          description: 'Seconds to wait (RATE_LIMITED).'
    ImportJob:
      type: object
      properties:
        job_uid:
          type: string
        status:
          type: string
          enum:
            - queued
            - running
            - done
            - failed
            - cancelled
        progress:
          type:
            - object
            - 'null'
          additionalProperties: true
      additionalProperties: true
    List:
      type: object
      description: 'A mail list. Legacy-pinned shape — additional keys may be present.'
      properties:
        uid:
          type: string
        name:
          type: string
        default_subject:
          type:
            - string
            - 'null'
        from_email:
          type: string
          format: email
        from_name:
          type: string
        subscribers_count:
          type: integer
        created_at:
          type: string
      additionalProperties: true
    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        last_page:
          type: integer
    QuotaMeter:
      type: object
      description: 'A plan quota. `granted=false` means the plan does not include the feature; `unlimited=true` means no cap (limit is null).'
      properties:
        used:
          type: integer
        limit:
          type:
            - integer
            - 'null'
        unlimited:
          type: boolean
        granted:
          type: boolean
    Scope:
      type: string
      description: 'API key scope. Implication rules (a key with the left scope also satisfies the right): `all` → `contacts`, `audiences`, `emails`, `domains`, `sends`, `templates`, `automations`, `triggers`, `webhooks`, `analytics`, `usage`, `brand`. `contacts` → `audiences`. `emails` → `domains`, `sends`, `templates`. `automations` → `triggers`.'
      enum:
        - all
        - contacts
        - audiences
        - emails
        - domains
        - sends
        - templates
        - automations
        - triggers
        - webhooks
        - analytics
        - usage
        - brand
    Segment:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        matching:
          type: string
          enum:
            - all
            - any
        list_uid:
          type: string
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/SegmentCondition'
        subscribers_count:
          type: integer
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    SegmentCondition:
      type: object
      required:
        - type
        - operator
      description: |-
        A single segment condition. Allowed operators per type:
        - `field`: `equal`, `not_equal`, `contains`, `not_contains`, `starts`, `ends`, `not_starts`, `not_ends`, `greater`, `less`, `blank`, `not_blank`
        - `date_field`: `greater`, `less`, `equal`, `not_equal`, `blank`, `not_blank`
        - `verification`: `verification_equal`, `verification_not_equal`
        - `tag`: `tag_contains`, `tag_not_contains`
        - `last_open_email`: `last_open_email_greater_than_days`, `last_open_email_less_than_days`
        - `last_link_click`: `last_link_click_greater_than_days`, `last_link_click_less_than_days`
        - `created_date`: `created_date_greater`, `created_date_less`, `created_date_last_x_days`

        `field` conditions on date/datetime fields use the `date_field` operator set. Operators `blank` and `not_blank` need no value. Day-count operators take a positive integer; absolute `created_date_greater`/`created_date_less` take an ISO 8601 datetime.
      properties:
        type:
          type: string
          enum:
            - field
            - verification
            - tag
            - last_open_email
            - last_link_click
            - created_date
        field_uid:
          type: string
          description: 'Required when type=field: uid of a custom field of this list.'
        field_tag:
          type: string
          readOnly: true
        operator:
          type: string
          enum:
            - blank
            - contains
            - created_date_greater
            - created_date_last_x_days
            - created_date_less
            - ends
            - equal
            - greater
            - last_link_click_greater_than_days
            - last_link_click_less_than_days
            - last_open_email_greater_than_days
            - last_open_email_less_than_days
            - less
            - not_blank
            - not_contains
            - not_ends
            - not_equal
            - not_starts
            - starts
            - tag_contains
            - tag_not_contains
            - verification_equal
            - verification_not_equal
        value:
          type:
            - string
            - 'null'
    Subscriber:
      type: object
      description: 'A subscriber. Custom-field values appear as additional top-level keys by tag (e.g. FIRST_NAME). Legacy-pinned shape.'
      properties:
        uid:
          type: string
        email:
          type: string
          format: email
        status:
          type: string
          enum:
            - subscribed
            - unsubscribed
            - unconfirmed
            - blacklisted
            - spam-reported
        list_uid:
          type: string
        tags:
          type:
            - array
            - 'null'
          items:
            type: string
        created_at:
          type: string
      additionalProperties: true
    Template:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        source:
          type: string
          enum:
            - gallery
            - ai
            - imported
            - api
          description: 'Provenance: gallery (template gallery), imported (ZIP/HTML upload), ai (AI Studio / builder), api (created through this API).'
        thumbnail:
          type:
            - string
            - 'null'
          format: uri
        html:
          type: string
          description: 'Full template HTML. Only present when `include=html`.'
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    Usage:
      type: object
      properties:
        plan:
          type: object
          properties:
            name:
              type:
                - string
                - 'null'
            status:
              type:
                - string
                - 'null'
            period_ends_at:
              type:
                - string
                - 'null'
              format: date-time
        quotas:
          type: object
          description: 'Keys: lists, campaigns, subscribers, automations, sending_domains, users, api_keys.'
          additionalProperties:
            $ref: '#/components/schemas/QuotaMeter'
        credits:
          type: object
          description: 'Keyed by credit name (e.g. send_email, email_verification, ai_generation).'
          additionalProperties:
            $ref: '#/components/schemas/CreditMeter'
    WebhookDelivery:
      type: object
      properties:
        uid:
          type: string
        event:
          type: string
        status:
          type: string
          enum:
            - pending
            - success
            - failed
        attempts:
          type: integer
        response_code:
          type:
            - integer
            - 'null'
        duration_ms:
          type:
            - integer
            - 'null'
        error:
          type:
            - string
            - 'null'
        created_at:
          type:
            - string
            - 'null'
          format: date-time
    WebhookSubscription:
      type: object
      properties:
        uid:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
              - email.delivered
              - email.failed
              - email.opened
              - email.clicked
              - email.bounced
              - email.complained
              - contact.unsubscribed
              - contact.subscribed
        active:
          type: boolean
        disabled:
          type: boolean
          description: 'True after auto-disable (20 consecutive failures). Re-enable with `PATCH {active: true}`.'
        disabled_at:
          type:
            - string
            - 'null'
          format: date-time
        failure_count:
          type: integer
          description: 'Consecutive failed deliveries; resets to 0 on any success.'
        last_success_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
