---
openapi: 3.0.3
info:
  title: Vero Open API v2
  version: 2.0.0
  description:
    API for interfacing with Vero to design, update and manage broadcasts, journeys, and topics.
servers:
  - url: https://api.getvero.com/api/v2
tags:
  - name: Broadcasts
  - name: Broadcast Messages
  - name: Broadcast Message Contents
  - name: Journeys
  - name: Journey Messages
  - name: Journey Message Contents
  - name: Campaigns
  - name: Campaign Messages
  - name: Campaign Message Contents
  - name: Topics
security:
  - ApiKeyAuth: []
paths:
  "/broadcasts":
    get:
      summary: List broadcasts
      tags:
        - Broadcasts
      operationId: listBroadcasts
      description: |
        Lists broadcasts for your project, returning the newest first. Cursor-paginated: no total count is exposed.
        Archived broadcasts are excluded by default but can be requested explicitly with `?status=archived`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size (1-100, default 25).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: sort
          in: query
          required: false
          description: Sort order.
          schema:
            type: string
            enum:
              - updated_at_desc
              - updated_at_asc
              - created_at_desc
              - created_at_asc
              - name_asc
              - name_desc
            default: updated_at_desc
        - name: status
          in: query
          required: false
          description:
            Filter by status. Comma-separated; archived broadcasts are excluded unless `archived` is
            included. Invalid values return 422.
          schema:
            type: string
            example: draft,active,paused
        - name: schedule.type
          in: query
          required: false
          description:
            Filter by schedule type. Comma-separated; both are included if omitted. Invalid values
            return 422.
          schema:
            type: string
            example: single,recurring
        - name: cursor
          in: query
          required: false
          description: Opaque pagination cursor from a previous response's `next_cursor`.
          schema:
            type: string
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcasts listed.
          content:
            application/json:
              examples:
                default:
                  value:
                    data:
                      - id: 42
                        name: Welcome to the weekly digest
                        channel: email
                        schedule:
                          type: single
                          timezone_detection: false
                        status: active
                        labels:
                          - weekly-digest
                        created_at: "2026-03-01T10:00:00Z"
                        updated_at: "2026-03-01T10:00:00Z"
                    has_more: false
                    next_cursor:
              schema:
                "$ref": "#/components/schemas/BroadcastListResponse"
        "422":
          description: Invalid query parameters.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
    post:
      summary: Create a broadcast
      tags:
        - Broadcasts
      operationId: createBroadcast
      description:
        Creates an unscheduled broadcast draft for your project, optionally seeding the default
        first message and default content from `message.content.body`. At this time `audience` and
        `schedule` fields are rejected with `422`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Broadcast draft created.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 42
                    name: Summer promo
                    channel: email
                    schedule:
                      type: single
                      timezone_detection: false
                    status: draft
                    labels: []
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                "$ref": "#/components/schemas/Broadcast"
        "422":
          description: Name is required and must be non-blank.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateBroadcastRequest"
            examples:
              minimal:
                summary: Minimal draft
                value:
                  name: Summer promo
              withContent:
                summary: Draft with initial content
                value:
                  name: Summer promo
                  channel: email
                  message:
                    from: hello@acme.com
                    content:
                      subject: 20% off this weekend
                      preheader_text: Sale ends Sunday
                      body:
                        html: "<p>Hi {{ first_name }}</p>"
        required: true
  "/broadcasts/{id}":
    get:
      summary: Get a broadcast
      tags:
        - Broadcasts
      operationId: getBroadcast
      description:
        'Returns a single broadcast. Archived broadcasts return 200 with `status: "archived"`.'
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Single broadcast returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 42
                    name: Welcome to the weekly digest
                    channel: email
                    schedule:
                      type: single
                      timezone_detection: false
                    status: draft
                    labels:
                      - weekly-digest
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                "$ref": "#/components/schemas/Broadcast"
        "404":
          description: Resource is not an eligible broadcast or belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "503":
          description: API key validation is temporarily unavailable.
    patch:
      summary: Update a broadcast
      tags:
        - Broadcasts
      operationId: updateBroadcast
      description: |
        Updates broadcast metadata (`name` and a constrained `channel`). `audience` and `schedule` fields
        are rejected with `422`. A `channel` change is accepted only while the broadcast is a single-message
        draft whose current message is not yet sendable: switching channels will reset the
        message content body. This endpoint cannot be used to update broadcast content. See dedicated endpoints.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast updated.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Broadcast"
        "404":
          description: Resource is not an eligible broadcast or belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "409":
          description: Broadcast is no longer a draft.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "422":
          description: Unsupported fields are rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/UpdateBroadcastRequest"
            examples:
              rename:
                summary: Rename
                value:
                  name: Updated broadcast name
        required: true
  "/broadcasts/{broadcast_id}/messages":
    get:
      summary: List messages
      tags:
        - Broadcast Messages
      operationId: listBroadcastMessages
      description:
        Lists a broadcast's messages (A/B test arms), ordered by `position` ascending and then `id`.
        `from` and `reply_to` are email-only and will be `null` for non-email channels.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast messages returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 100
                      broadcast_id: 42
                      position: 1
                      name: Control
                      active: true
                      from: hello@acme.com
                      reply_to:
                      created_at: "2026-03-01T10:00:00Z"
                      updated_at: "2026-03-01T10:00:00Z"
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/BroadcastMessage"
        "404":
          description: The resource is not a broadcast or belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
    post:
      summary: Create a message
      tags:
        - Broadcast Messages
      operationId: createBroadcastMessage
      description: |
        Creates an additional message (A/B test arm) on a draft broadcast by cloning the
        control message's default content (`subject`, `body`, `sender`).
        Optionally provide `from`, `reply_to` and `content` (`subject`, `preheader_text`, `name`, `body`) fields
        to override the cloned defaults. Editable only while the broadcast is a draft.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Broadcast message created.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 101
                    broadcast_id: 42
                    position: 2
                    name: Variation A
                    active: true
                    from: hello@acme.com
                    reply_to: replies@acme.com
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                "$ref": "#/components/schemas/BroadcastMessage"
        "422":
          description: Unverified sender is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateBroadcastMessageRequest"
            examples:
              withOverrides:
                summary: A/B arm with overrides
                value:
                  from: promo@acme.com
                  content:
                    subject: Variant B
                    body:
                      html: "<p>Variant</p>"
  "/broadcasts/{broadcast_id}/messages/{id}":
    get:
      summary: Get a message
      tags:
        - Broadcast Messages
      operationId: getBroadcastMessage
      description: Reads one message (A/B test arm) of a broadcast.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast message returned.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BroadcastMessage"
        "404":
          description: Message belongs to a different broadcast.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
    patch:
      summary: Update a message
      tags:
        - Broadcast Messages
      operationId: updateBroadcastMessage
      description: |
        Updates a message's sender (`from` and `reply_to`) and `active` flag on a draft broadcast.
        Changes to the sender attributes apply across all of the message's contents. The control
        and the last remaining active message cannot be deactivated. `name`,
        `position`, and `test_name` are derived and are rejected with `422`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Sender updated across every content.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BroadcastMessage"
        "422":
          description: Unverified sender is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "409":
          description: The control message cannot be deactivated.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/UpdateBroadcastMessageRequest"
            examples:
              sender:
                summary: Change the sender
                value:
                  from: promo@acme.com
                  reply_to: replies@acme.com
              deactivate:
                summary: Deactivate an arm
                value:
                  active: false
        required: true
  "/broadcasts/{broadcast_id}/messages/{message_id}/contents":
    get:
      summary: List localized contents
      tags:
        - Broadcast Message Contents
      operationId: listBroadcastContent
      description: |
        Returns active content records (localization variants) for a broadcast message. Ordered with default content first,
        then localized content sorted by `locale` ascending and then `id` ascending.
        There is one content record per locale as well as a default. The list response omits `content`. Use the dedicated endpoint to retrieve the contents.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast message content returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 200
                      message_id: 100
                      locale:
                      is_default: true
                      name: Default
                      subject: Welcome to Acme
                      preheader_text: Glad you're here
                      created_at: "2026-03-01T10:00:00Z"
                      updated_at: "2026-03-01T10:00:00Z"
                    - id: 201
                      message_id: 100
                      locale: fr-FR
                      is_default: false
                      name: French
                      subject: Bienvenue chez Acme
                      preheader_text: Ravis de vous voir
                      created_at: "2026-03-01T10:00:00Z"
                      updated_at: "2026-03-01T10:00:00Z"
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/BroadcastContentListItem"
        "404":
          description: Message belongs to a different broadcast.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
    post:
      summary: Create localized content
      tags:
        - Broadcast Message Contents
      operationId: createBroadcastContent
      description: |
        Creates localized broadcast message content by cloning the message's default content.
        `locale` is required. Fields you provide will override the cloned default. The `content` field must be exactly one of `mjml` (email), `html` (email), `text` (email and SMS), or `payload` (push).
        Content can only be created while the broadcast is a draft. Duplicate locales are rejected with `409 Conflict`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Broadcast content created.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BroadcastContent"
        "422":
          description: Missing locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "409":
          description: Duplicate locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateBroadcastContentRequest"
            examples:
              htmlContent:
                summary: HTML content
                value:
                  locale: fr-FR
                  name: French
                  subject: Bonjour
                  content:
                    html: "<p>Bonjour</p>"
        required: true
  "/broadcasts/{broadcast_id}/messages/{message_id}/contents/{id}":
    get:
      summary: Get localized content
      tags:
        - Broadcast Message Contents
      operationId: getBroadcastContent
      description: Reads one content of a broadcast message, including the `content` field.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Content ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast message content returned with body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BroadcastContent"
        "404":
          description: Content belongs to a different message.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
    patch:
      summary: Update localized content
      tags:
        - Broadcast Message Contents
      operationId: updateBroadcastContent
      description: |
        Partially updates localized broadcast message content. Optional `content` must be exactly
        one of `mjml`, `html`, `text`, or `payload` as the UI infers the editor type from that key.
        Content can only be updated while the broadcast is a draft.
        You cannot add a `locale` value on the default message content and you cannot clear the `locale`
        field on a localized content record. Duplicate `locale` values are rejected with
        `409 Conflict`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: broadcast_id
          in: path
          required: true
          description: Broadcast ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Content ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Broadcast content updated.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BroadcastContent"
        "422":
          description: Default content cannot gain a locale.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "409":
          description: Duplicate locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/UpdateBroadcastContentRequest"
            examples:
              HTML:
                summary: Update subject and body
                value:
                  subject: Updated
                  content:
                    html: "<p>Updated</p>"
        required: true
  "/journeys":
    get:
      summary: List journeys
      tags:
        - Journeys
      operationId: listJourneys
      description:
        Lists the journeys in your project, sorted by the date last updated in descending order.
        Each item in the returned list is a summary including the `id`, `name`, `description`,
        `status` and timestamps of the journey, omitting the journey steps (represented as a graph)
        and message content. Results are cursor-paginated and do not include a total count. Archived
        journeys are excluded by default but can be requested explicitly with `?status=archived`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size (1-100, default 25).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: sort
          in: query
          required: false
          description: Sort order.
          schema:
            type: string
            enum:
              - updated_at_desc
              - updated_at_asc
              - created_at_desc
              - created_at_asc
              - name_asc
              - name_desc
            default: updated_at_desc
        - name: status
          in: query
          required: false
          description:
            Comma-separated statuses to include, e.g. `draft,active`. Archived journeys are excluded
            unless `archived` is requested.
          schema:
            type: string
            example: draft,active
        - name: cursor
          in: query
          required: false
          description: Opaque pagination cursor from a previous response's `next_cursor`.
          schema:
            type: string
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journeys listed.
          content:
            application/json:
              examples:
                default:
                  value:
                    data:
                      - id: 42
                        name: Welcome series
                        description:
                        status: active
                        created_at: "2026-03-01T10:00:00Z"
                        updated_at: "2026-03-01T10:00:00Z"
                    has_more: false
                    next_cursor:
              schema:
                "$ref": "#/components/schemas/JourneyListResponse"
        "422":
          description: Invalid query parameters.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
  "/journeys/{id}":
    get:
      summary: Get a journey
      tags:
        - Journeys
      operationId: getJourney
      description:
        'Retrieves a single journey including its full graph. The graph include the `trigger`,
        `nodes` and `edges`. The graph is composed of nodes and edges. Messaging nodes expose
        message and content metadata but do not include content bodies or audience
        details.  Archived journeys return 200 with `status: "archived"`.'
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Single journey returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 42
                    name: Welcome series
                    description:
                    status: active
                    trigger:
                      type: event
                      event_name: user_signed_up
                    nodes:
                      - id: 1
                        type: enter
                      - id: 2
                        type: delay
                        delay:
                          duration: 3
                          duration_unit: days
                          delivery_option: 1
                          delay_property:
                          property_type:
                          preferred_delivery_time:
                      - id: 3
                        type: email
                        title: Welcome email
                        message:
                          id: 100
                          from: hello@example.com
                          reply_to: reply@example.com
                          contents:
                            - id: 200
                              locale:
                              is_default: true
                              subject: Welcome aboard
                              preheader_text: Glad you're here
                      - id: 4
                        type: branch
                        conditions:
                          combinator: and
                          groups: []
                      - id: 5
                        type: exit
                    edges:
                      - from: 1
                        to: 2
                      - from: 2
                        to: 4
                      - from: 4
                        to: 3
                        branch_index: 0
                      - from: 4
                        to: 5
                        branch_index: 1
                      - from: 3
                        to: 5
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                type: object
                required:
                  - id
                  - name
                  - description
                  - status
                  - trigger
                  - nodes
                  - edges
                  - created_at
                  - updated_at
                properties:
                  id:
                    type: integer
                  name:
                    type: string
                  description:
                    type: string
                    nullable: true
                  status:
                    type: string
                    enum:
                      - draft
                      - active
                      - paused
                      - archived
                      - cancelled
                  trigger:
                    type: object
                    nullable: true
                    required:
                      - type
                      - event_name
                    properties:
                      type:
                        type: string
                        enum:
                          - event
                      event_name:
                        type: string
                  nodes:
                    type: array
                    items:
                      type: object
                      required:
                        - id
                        - type
                      properties:
                        id:
                          type: integer
                        type:
                          type: string
                          enum:
                            - enter
                            - exit
                            - email
                            - push
                            - sms
                            - delay
                            - branch
                            - ab_test
                            - ab_test_end
                        title:
                          type: string
                          nullable: true
                        delay:
                          type: object
                          nullable: true
                        conditions:
                          type: object
                          nullable: true
                        message:
                          type: object
                          nullable: true
                          required:
                            - id
                            - from
                            - reply_to
                            - contents
                          properties:
                            id:
                              type: integer
                            from:
                              type: string
                              nullable: true
                            reply_to:
                              type: string
                              nullable: true
                            contents:
                              type: array
                              items:
                                type: object
                                required:
                                  - id
                                  - locale
                                  - is_default
                                  - subject
                                  - preheader_text
                                properties:
                                  id:
                                    type: integer
                                  locale:
                                    type: string
                                    nullable: true
                                  is_default:
                                    type: boolean
                                  subject:
                                    type: string
                                    nullable: true
                                  preheader_text:
                                    type: string
                                    nullable: true
                  edges:
                    type: array
                    items:
                      type: object
                      required:
                        - from
                        - to
                      properties:
                        from:
                          type: integer
                        to:
                          type: integer
                        branch_index:
                          type: integer
                  created_at:
                    type: string
                  updated_at:
                    type: string
        "404":
          description: Journey belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
  "/journeys/{journey_id}/messages":
    get:
      summary: List messages
      tags:
        - Journey Messages
      operationId: listJourneyMessages
      description:
        Lists all message nodes within a journey. These are ordered by `node_id`, in ascending
        order. Returns message metadata only (including `id`, `from` and `reply_to`) but never
        content body attributes. The `from` and `reply_to` are email-only fields and will return
        `null` for SMS and push message nodes.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journey messages returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 100
                      from: hello@example.com
                      reply_to: reply@example.com
                      contents:
                        - id: 200
                          locale:
                          is_default: true
                          subject: Welcome aboard
                          preheader_text: Glad you're here
              schema:
                type: array
                items:
                  type: object
                  required:
                    - id
                    - from
                    - reply_to
                    - contents
                  properties:
                    id:
                      type: integer
                    from:
                      type: string
                      nullable: true
                    reply_to:
                      type: string
                      nullable: true
                    contents:
                      type: array
                      items:
                        type: object
                        required:
                          - id
                          - locale
                          - is_default
                          - subject
                          - preheader_text
                        properties:
                          id:
                            type: integer
                          locale:
                            type: string
                            nullable: true
                          is_default:
                            type: boolean
                          subject:
                            type: string
                            nullable: true
                          preheader_text:
                            type: string
                            nullable: true
        "404":
          description: Journey belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
  "/journeys/{journey_id}/messages/{id}":
    get:
      summary: Get a message
      tags:
        - Journey Messages
      operationId: getJourneyMessage
      description:
        Reads a single message in a journey. Returns message metadata only and does not include
        content bodies.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journey message returned.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - from
                  - reply_to
                  - contents
                properties:
                  id:
                    type: integer
                  from:
                    type: string
                    nullable: true
                  reply_to:
                    type: string
                    nullable: true
                  contents:
                    type: array
                    items:
                      type: object
                      required:
                        - id
                        - locale
                        - is_default
                        - subject
                        - preheader_text
                      properties:
                        id:
                          type: integer
                        locale:
                          type: string
                          nullable: true
                        is_default:
                          type: boolean
                        subject:
                          type: string
                          nullable: true
                        preheader_text:
                          type: string
                          nullable: true
        "404":
          description: Message is not part of this journey.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
  "/journeys/{journey_id}/messages/{message_id}/contents":
    get:
      summary: List localized contents
      tags:
        - Journey Message Contents
      operationId: listJourneyContent
      description:
        Returns active content records (localization variants) for a journey message. Ordered with
        default content first, then localized content sorted by `locale` ascending and then `id`
        ascending. There is one content record per locale as well as a default. The list response
        omits `content`. Use the dedicated endpoint to retrieve the contents.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journey message content returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 200
                      locale:
                      is_default: true
                      subject: Welcome aboard
                      preheader_text: Glad you're here
                    - id: 201
                      locale: fr
                      is_default: false
                      subject: Bienvenue
                      preheader_text: Ravi
              schema:
                type: array
                items:
                  type: object
                  required:
                    - id
                    - locale
                    - is_default
                    - subject
                    - preheader_text
                  properties:
                    id:
                      type: integer
                    locale:
                      type: string
                      nullable: true
                    is_default:
                      type: boolean
                    subject:
                      type: string
                      nullable: true
                    preheader_text:
                      type: string
                      nullable: true
        "404":
          description: Message is not part of this journey.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
    post:
      summary: Create localized content
      tags:
        - Journey Message Contents
      operationId: createJourneyContent
      x-mint:
        content: |
          <Warning>
          This feature only works when editing journeys created in Vero 1.0 (known as workflows) at the present time. You can add content to journeys created in Vero 2.0 but this feature is not yet supported in the Vero 2.0 UI.
          </Warning>
      description: |
        Creates localized journey message content by cloning the message's default content.
        `locale` is required. Fields you provide will override the cloned default. The `content` field must be exactly one of `mjml` (email), `html` (email), `text` (email and SMS), or `payload` (push).
        Unlike broadcasts, a journey does not need to be a draft to create localized content. A live or paused journey is editable. Archived and locked journeys are rejected with `409 Conflict`, as are duplicate locales.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Journey content created.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 202
                    locale: fr-FR
                    is_default: false
                    subject: Bonjour
                    preheader_text: Ravi de vous voir
                    content:
                      html: "<p>Bonjour</p>"
              schema:
                type: object
                required:
                  - id
                  - locale
                  - is_default
                  - subject
                  - preheader_text
                  - content
                properties:
                  id:
                    type: integer
                  locale:
                    type: string
                    nullable: true
                  is_default:
                    type: boolean
                  subject:
                    type: string
                    nullable: true
                  preheader_text:
                    type: string
                    nullable: true
                  content:
                    type: object
                    description:
                      Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
                    properties:
                      mjml:
                        type: string
                      html:
                        type: string
                      text:
                        type: string
                      payload:
                        type: object
        "422":
          description: Missing locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "409":
          description: Duplicate locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - locale
              properties:
                locale:
                  type: string
                  maxLength: 255
                  description:
                    Locale for the new content. Required, and must not already exist on this
                    message.
                subject:
                  type: string
                  nullable: true
                  description: Subject line. Omit to inherit the default content's.
                preheader_text:
                  type: string
                  nullable: true
                  description: Preheader text. Omit to inherit the default content's.
                content:
                  type: object
                  description:
                    Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
                  properties:
                    mjml:
                      type: string
                    html:
                      type: string
                    text:
                      type: string
                    payload:
                      type: object
            examples:
              htmlContent:
                summary: HTML content
                value:
                  locale: fr-FR
                  subject: Bonjour
                  content:
                    html: "<p>Bonjour</p>"
        required: true
  "/journeys/{journey_id}/messages/{message_id}/contents/{id}":
    get:
      summary: Get localized content
      tags:
        - Journey Message Contents
      operationId: getJourneyContent
      description:
        Reads one content of a broadcast message, including the `content` field. This is the only
        journey endpoint that emits a content body.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Content ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journey message content returned with body.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 200
                    locale:
                    is_default: true
                    subject: Welcome aboard
                    preheader_text: Glad you're here
                    content:
                      html: "<h1>Welcome</h1>"
              schema:
                type: object
                required:
                  - id
                  - locale
                  - is_default
                  - subject
                  - preheader_text
                  - content
                properties:
                  id:
                    type: integer
                  locale:
                    type: string
                    nullable: true
                  is_default:
                    type: boolean
                  subject:
                    type: string
                    nullable: true
                  preheader_text:
                    type: string
                    nullable: true
                  content:
                    type: object
                    description:
                      Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
                    properties:
                      mjml:
                        type: string
                      html:
                        type: string
                      text:
                        type: string
                      payload:
                        type: object
        "404":
          description: Content belongs to a different message.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
    patch:
      summary: Update localized content
      tags:
        - Journey Message Contents
      operationId: updateJourneyContent
      x-mint:
        content: |
          <Warning>
          This feature only works when editing journeys created in Vero 1.0 (known as workflows) at the present time. You can add content to journeys created in Vero 2.0 but this feature is not yet supported in the Vero 2.0 UI.
          </Warning>
      description: |
        Partially updates journey message content. Optional `content` must be exactly one of `mjml`, `html`, `text`, or `payload` as the UI infers the editor type from that key.
        Unlike broadcasts, a journey does not need to be a draft in order to edit message content. A live or paused journey is editable. Archived and locked journeys are rejected with `409 Conflict`.
        You cannot add a `locale` value to the default content and you cannot clear the `locale` on a localized content record. Duplicate `locale` values are rejected with `409 Conflict`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: journey_id
          in: path
          required: true
          description: Journey ID.
          schema:
            type: integer
        - name: message_id
          in: path
          required: true
          description: Message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Content ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Journey content updated.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 201
                    locale: fr
                    is_default: false
                    subject: Bonjour
                    preheader_text: Ravi
                    content:
                      html: "<p>Bonjour</p>"
              schema:
                type: object
                required:
                  - id
                  - locale
                  - is_default
                  - subject
                  - preheader_text
                  - content
                properties:
                  id:
                    type: integer
                  locale:
                    type: string
                    nullable: true
                  is_default:
                    type: boolean
                  subject:
                    type: string
                    nullable: true
                  preheader_text:
                    type: string
                    nullable: true
                  content:
                    type: object
                    description:
                      Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
                    properties:
                      mjml:
                        type: string
                      html:
                        type: string
                      text:
                        type: string
                      payload:
                        type: object
        "422":
          description: Default content cannot gain a locale.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "409":
          description: Duplicate locale is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                locale:
                  type: string
                  maxLength: 255
                  description:
                    New locale for a localized content. The default content's locale cannot be
                    changed, and a locale already used on this message is rejected.
                subject:
                  type: string
                  nullable: true
                  description: Subject line. Omit to leave it unchanged.
                preheader_text:
                  type: string
                  nullable: true
                  description: Preheader text. Omit to leave it unchanged.
                content:
                  type: object
                  description:
                    Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
                  properties:
                    mjml:
                      type: string
                    html:
                      type: string
                    text:
                      type: string
                    payload:
                      type: object
            examples:
              htmlContent:
                summary: Update subject and body
                value:
                  subject: Bonjour
                  content:
                    html: "<p>Bonjour</p>"
        required: true
  "/campaigns/{id}":
    get:
      summary: Get a campaign
      tags:
        - Campaigns
      operationId: getCampaign
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      description: Returns a single campaign for your project.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Campaign ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 123
                    title: Holiday Sale
                    status: draft
                    channel: email
                    labels:
                      - promotions
                    scheduled_at:
                    created_at: "2026-03-26T01:02:03Z"
                    updated_at: "2026-03-26T01:02:03Z"
              schema:
                "$ref": "#/components/schemas/Campaign"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "503":
          description: API key validation is temporarily unavailable.
  "/campaigns/{campaign_id}/campaign-messages":
    get:
      summary: List messages
      tags:
        - Campaign Messages
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: listCampaignMessages
      description: Lists a campaign's messages, ordered by position then id.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: campaign_id
          in: path
          required: true
          description: Campaign ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign messages returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 456
                      title: Control
                      position: 1
                      active: true
                    - id: 457
                      title: Variation A
                      position: 2
                      active: true
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/CampaignMessage"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "503":
          description: API key validation is temporarily unavailable.
  "/campaign-messages/{id}":
    get:
      summary: Get a message
      tags:
        - Campaign Messages
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: getCampaignMessage
      description: Returns a single campaign message.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Campaign message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign message returned.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignMessage"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "503":
          description: API key validation is temporarily unavailable.
  "/campaign-messages/{campaign_message_id}/content":
    get:
      summary: List localized contents
      tags:
        - Campaign Message Contents
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: listCampaignContent
      description: |
        Returns active content records for the message (one per locale / default).

        The list response omits `content`; use `GET .../content/:id` to load the body.

        Ordering:
        - default content first
        - localized content next, sorted by locale then id
      security:
        - ApiKeyAuth: []
      parameters:
        - name: campaign_message_id
          in: path
          required: true
          description: Campaign message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign message content returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    - id: 789
                      locale:
                      name: Default
                      is_default: true
                      subject: Welcome
                      preheader_text: Hello
                      created_at: "2026-03-26T01:02:03Z"
                      updated_at: "2026-03-26T01:02:03Z"
                    - id: 790
                      locale: fr-FR
                      name: French
                      is_default: false
                      subject: Bonjour
                      preheader_text: Salut
                      created_at: "2026-03-26T01:02:03Z"
                      updated_at: "2026-03-26T01:02:03Z"
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/CampaignContentListItem"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "503":
          description: API key validation is temporarily unavailable.
    post:
      summary: Create localized content
      tags:
        - Campaign Message Contents
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: createCampaignContent
      description: |
        Creates localized content by cloning the message's default content.

        Omitted fields inherit from the cloned default. Optional `content` uses
        exactly one of `mjml`, `html`, `text`, or `payload` (see `CampaignContentBody`).
        Content can only be created while the parent campaign is in `draft`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: campaign_message_id
          in: path
          required: true
          description: Campaign message ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Campaign content created.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignContent"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "409":
          description: Content for this locale already exists on the message.
        "422":
          description:
            Invalid request payload, the campaign is not in draft, or unsupported content format.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "503":
          description: API key validation is temporarily unavailable.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateCampaignContentRequest"
            examples:
              htmlContent:
                summary: HTML content
                value:
                  locale: fr-FR
                  name: French
                  subject: Bonjour
                  content:
                    html: "<p>Bonjour</p>"
        required: true
  "/campaign-messages/{campaign_message_id}/content/{id}":
    get:
      summary: Get localized content
      tags:
        - Campaign Message Contents
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: getCampaignContent
      security:
        - ApiKeyAuth: []
      parameters:
        - name: campaign_message_id
          in: path
          required: true
          description: Campaign message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Campaign content record ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign content returned (MJML).
          content:
            application/json:
              examples:
                HTML:
                  value:
                    id: 789
                    locale: fr-FR
                    name: French
                    is_default: false
                    subject: Bonjour
                    preheader_text: Hello
                    content:
                      html: "<p>Bonjour</p>"
                    created_at: "2026-03-26T01:02:03Z"
                    updated_at: "2026-03-26T01:02:03Z"
                Push:
                  value:
                    id: 790
                    locale:
                    name: Push Default
                    is_default: true
                    subject:
                    preheader_text:
                    content:
                      payload:
                        payloads:
                          ios:
                            aps:
                              alert:
                                body: Hello
                    created_at: "2026-03-26T01:02:03Z"
                    updated_at: "2026-03-26T01:02:03Z"
              schema:
                "$ref": "#/components/schemas/CampaignContent"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "422":
          description:
            Invalid request payload, the campaign is not in draft, or unsupported content format.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "503":
          description: API key validation is temporarily unavailable.
    patch:
      summary: Update localized content
      tags:
        - Campaign Message Contents
      x-mint:
        content: |
          <Warning>
          This endpoint will continue to work but is being replaced by dedicated Broadcasts and Journeys endpoints. Refer to these new endpoints in this API reference.
          </Warning>
      operationId: updateCampaignContent
      description: |
        Partially updates message content.

        Optional `content` uses exactly one of `mjml`, `html`, `text`, or `payload`; the API
        infers the editor type from that key. Content can only be updated while the parent
        campaign is in `draft`. Non-workflow messages reject duplicate locales with `409 Conflict`.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: campaign_message_id
          in: path
          required: true
          description: Campaign message ID.
          schema:
            type: integer
        - name: id
          in: path
          required: true
          description: Campaign content record ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Campaign content updated.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignContent"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "404":
          description: Resource not found in this project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "409":
          description: Content for this locale already exists on the message.
        "422":
          description:
            Invalid request payload, the campaign is not in draft, or unsupported content format.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "503":
          description: API key validation is temporarily unavailable.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/UpdateCampaignContentRequest"
            examples:
              HTML:
                summary: HTML
                value:
                  subject: Updated
                  content:
                    html: "<p>Updated</p>"
              Push:
                summary: Push
                value:
                  content:
                    payload:
                      payloads:
                        ios:
                          aps:
                            dasherized-key: dashed
                            underscored_key: underscored
        required: true
  "/topics":
    get:
      summary: List topics
      tags:
        - Topics
      operationId: listTopics
      description: |
        Lists preference center topics for your project. The list is cursor-paginated. No total count is exposed.
        Topics cannot be deleted.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size (between `1` and `100`. The default is `25`).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: sort
          in: query
          required: false
          description: Desired sort order.
          schema:
            type: string
            enum:
              - position_asc
              - position_desc
              - updated_at_desc
              - updated_at_asc
              - created_at_desc
              - created_at_asc
            default: position_asc
        - name: cursor
          in: query
          required: false
          description: Pagination cursor from a previous response's `next_cursor`.
          schema:
            type: string
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Topics listed.
          content:
            application/json:
              examples:
                default:
                  value:
                    data:
                      - id: 42
                        name: Product updates
                        description: Weekly digest
                        default_status: subscribed
                        created_at: "2026-03-01T10:00:00Z"
                        updated_at: "2026-03-01T10:00:00Z"
                    has_more: false
                    next_cursor:
              schema:
                "$ref": "#/components/schemas/TopicListResponse"
        "422":
          description: Invalid query parameters.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "503":
          description: API key validation is temporarily unavailable.
    post:
      summary: Create a topic
      tags:
        - Topics
      operationId: createTopic
      description:
        Creates a preference center topic for your project. `name` must be unique within the project
        (`name` is case insensitive). `default_status` defaults to `subscribed`. Topics cannot be
        deleted at this time.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "201":
          description: Topic created.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 42
                    name: Product updates
                    description: Weekly digest
                    default_status: subscribed
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                "$ref": "#/components/schemas/Topic"
        "422":
          description: Name is required and must be non-blank.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "401":
          description: Missing or invalid API key.
        "403":
          description: Missing campaign:write permission.
        "503":
          description: API key validation is temporarily unavailable.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateTopicRequest"
            examples:
              With description:
                summary: Name and description
                value:
                  name: Product updates
                  description: Weekly digest
              Default as unsubscribed:
                summary: Opt-out by default
                value:
                  name: Promos
                  default_status: unsubscribed
        required: true
  "/topics/{id}":
    get:
      summary: Get a topic
      tags:
        - Topics
      operationId: getTopic
      description: Returns a single preference center topic.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Topic ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Topic returned.
          content:
            application/json:
              examples:
                default:
                  value:
                    id: 42
                    name: Product updates
                    description: Weekly digest
                    default_status: subscribed
                    created_at: "2026-03-01T10:00:00Z"
                    updated_at: "2026-03-01T10:00:00Z"
              schema:
                "$ref": "#/components/schemas/Topic"
        "404":
          description: Topic belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "401":
          description: Missing or invalid API key.
        "403":
          description: The API key lacks permission to access this project.
        "503":
          description: API key validation is temporarily unavailable.
    patch:
      summary: Update a topic
      tags:
        - Topics
      operationId: updateTopic
      description:
        Updates a topic's `name`, `description` and/or `default_status`. Omitted fields are left
        unchanged. Pass `description` as `null` to clear it. Changing `default_status` affects
        profiles with no explicit choice for this topic. Topics cannot be deleted at this time.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Topic ID.
          schema:
            type: integer
        - name: revision
          in: header
          required: false
          description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
          schema:
            type: string
            pattern: "^\\d{4}-\\d{2}-\\d{2}$"
            example: "2026-03-01"
      responses:
        "200":
          description: Topic updated.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Topic"
        "422":
          description: Empty patch is rejected.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorList"
        "404":
          description: Topic belongs to another project.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NotFoundError"
        "401":
          description: Missing or invalid API key.
        "403":
          description: Missing campaign:write permission.
        "503":
          description: API key validation is temporarily unavailable.
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/UpdateTopicRequest"
            examples:
              rename:
                summary: Rename and describe
                value:
                  name: Product updates
                  description: Weekly digest
              clearDescription:
                summary: Clear description
                value:
                  description:
        required: true
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: Accepts either a raw API key or `Bearer <key>`.
  parameters:
    CampaignId:
      name: id
      in: path
      required: true
      description: Campaign ID.
      schema:
        type: integer
    CampaignIdScoped:
      name: campaign_id
      in: path
      required: true
      description: Campaign ID.
      schema:
        type: integer
    CampaignMessageId:
      name: id
      in: path
      required: true
      description: Campaign message ID.
      schema:
        type: integer
    CampaignMessageIdScoped:
      name: campaign_message_id
      in: path
      required: true
      description: Campaign message ID.
      schema:
        type: integer
    CampaignContentId:
      name: id
      in: path
      required: true
      description: Campaign content record ID.
      schema:
        type: integer
    Revision:
      name: revision
      in: header
      required: false
      description: Optional dated API revision for the request. Must use `YYYY-MM-DD` format.
      schema:
        type: string
        pattern: "^\\d{4}-\\d{2}-\\d{2}$"
        example: "2026-03-01"
  responses:
    Unauthorized:
      description: Missing or invalid API key.
    Forbidden:
      description: The API key lacks permission to access this project.
    NotFound:
      description: Resource not found in this project.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/NotFoundError"
          examples:
            default:
              value:
                error: not_found
    Conflict:
      description: Content for this locale already exists on the message.
    ValidationError:
      description:
        Invalid request payload, the campaign is not in draft, or unsupported content format.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ErrorList"
          examples:
            nonDraftCampaign:
              value:
                errors:
                  - message:
                      Campaign content can only be created or updated while the campaign is in draft
            unsupportedEditorType:
              value:
                errors:
                  - message: Unsupported content format
    ServiceUnavailable:
      description: API key validation is temporarily unavailable.
  schemas:
    Broadcast:
      type: object
      description: "A broadcast: a one-off or recurring message sent to an audience."
      required:
        - id
        - name
        - channel
        - schedule
        - status
        - labels
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        name:
          type: string
          description: Broadcast name.
        channel:
          type: string
          enum:
            - email
            - sms
            - push
          description: Delivery channel.
        schedule:
          type: object
          description: |
            Scheduling details for the broadcast. `type` indicates whether the
            broadcast is sent once or on a recurring schedule. Additional
            scheduling fields (such as `scheduled_at`, `cron`,
            `next_scheduled_at`, `batches`, and `timezone_detection`) may also be
            present.
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - single
                - recurring
              description:
                "`single` for a one-off broadcast, `recurring` for a broadcast sent on a repeating
                schedule."
          additionalProperties: true
        status:
          type: string
          enum:
            - draft
            - scheduled
            - sending
            - sent
            - cancelled
            - active
            - paused
            - archived
          description:
            Broadcast status. `archived` is returned for archived broadcasts on the detail endpoint.
        labels:
          type: array
          items:
            type: string
          description:
            Labels applied to the broadcast. Always present; `[]` when the broadcast has none.
            Labels are stored lowercased.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BroadcastListResponse:
      type: object
      description: Cursor-paginated list of broadcasts. No total count is exposed.
      required:
        - data
        - has_more
        - next_cursor
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Broadcast"
        has_more:
          type: boolean
          description: Whether more results exist beyond this page.
        next_cursor:
          type: string
          nullable: true
          description: Opaque cursor for the next page, or `null` when `has_more` is false.
    JourneySummary:
      type: object
      description:
        Summary of a journey — an automated, multi-step message flow. The list endpoint returns
        these summaries; they omit the journey's steps and message content.
      required:
        - id
        - name
        - description
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        name:
          type: string
          description: Journey name.
        description:
          type: string
          nullable: true
          description: Optional journey description. May be `null`.
        status:
          type: string
          enum:
            - draft
            - active
            - paused
            - cancelled
            - archived
          description:
            Journey lifecycle status. `archived` is returned only when explicitly requested via the
            `status` filter.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    JourneyListResponse:
      type: object
      description: Cursor-paginated list of journey summaries. No total count is exposed.
      required:
        - data
        - has_more
        - next_cursor
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/JourneySummary"
        has_more:
          type: boolean
          description: Whether more results exist beyond this page.
        next_cursor:
          type: string
          nullable: true
          description: Opaque cursor for the next page, or `null` when `has_more` is false.
    JourneyMessage:
      type: object
      description: |
        One message within a journey, as resolved from an email, SMS or push node.
        `from`/`reply_to` are email-only sender fields and are `null` for SMS/push
        messages. Returns metadata only — never content bodies.
      required:
        - id
        - from
        - reply_to
        - contents
      properties:
        id:
          type: integer
        from:
          type: string
          nullable: true
        reply_to:
          type: string
          nullable: true
        contents:
          type: array
          items:
            "$ref": "#/components/schemas/JourneyContentListItem"
    JourneyContentBody:
      type: object
      description: Channel content body — exactly one of `mjml`, `html`, `text`, or `payload`.
      properties:
        mjml:
          type: string
        html:
          type: string
        text:
          type: string
        payload:
          type: object
    JourneyContentListItem:
      type: object
      description: |
        Content metadata for a journey message (one per locale / default). The
        list response omits `content`; fetch a single record for the body.
      required:
        - id
        - locale
        - is_default
        - subject
        - preheader_text
      properties:
        id:
          type: integer
        locale:
          type: string
          nullable: true
        is_default:
          type: boolean
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
    JourneyContent:
      allOf:
        - "$ref": "#/components/schemas/JourneyContentListItem"
        - type: object
          required:
            - content
          properties:
            content:
              "$ref": "#/components/schemas/JourneyContentBody"
              description: "Full body: exactly one of `mjml`, `html`, `text`, or `payload` is set."
    BroadcastMessage:
      type: object
      description: |
        One message (A/B test arm) of a broadcast. `from`/`reply_to` are
        email-only sender fields and are `null` for SMS/push messages.
      required:
        - id
        - broadcast_id
        - position
        - name
        - active
        - from
        - reply_to
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        broadcast_id:
          type: integer
        position:
          type: integer
        name:
          type: string
          nullable: true
          description: Server-generated message name, for example `Control` or `Variation A`.
        active:
          type: boolean
        from:
          type: string
          nullable: true
          description: Sender email address (email messages only).
        reply_to:
          type: string
          nullable: true
          description: Reply-to email address (email messages only).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BroadcastContentBody:
      type: object
      description: |
        The deliverable body for a content record. Exactly one of `mjml`, `html`,
        `text`, or `payload` is present, depending on the message channel: email
        uses `mjml`/`html`/`text`, SMS uses `text`, push uses `payload`.
      properties:
        mjml:
          type: string
          nullable: true
        html:
          type: string
          nullable: true
        text:
          type: string
          nullable: true
        payload:
          type: object
          nullable: true
          additionalProperties: true
    BroadcastContentListItem:
      type: object
      description: |
        Content metadata for a broadcast message (one per locale / default). The
        list response omits `content`; fetch a single record for the body.
      required:
        - id
        - message_id
        - locale
        - is_default
        - name
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        message_id:
          type: integer
        locale:
          type: string
          nullable: true
          description: BCP-47-style locale tag, or `null` for the default content variant.
        is_default:
          type: boolean
        name:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BroadcastContent:
      allOf:
        - "$ref": "#/components/schemas/BroadcastContentListItem"
        - type: object
          required:
            - content
          properties:
            content:
              "$ref": "#/components/schemas/BroadcastContentBody"
              description: "Full body: exactly one of `mjml`, `html`, `text`, or `payload` is set."
    CreateBroadcastRequest:
      type: object
      description: |
        Create an unscheduled, unlaunched broadcast draft. Audience, segment,
        trigger, schedule, `send`, launch, and lifecycle fields are not supported
        and are rejected with `422 Validation error`.
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Broadcast name. Trimmed before validation; must be non-blank.
        channel:
          type: string
          enum:
            - email
            - sms
            - push
          default: email
          description: |
            Messaging channel. Defaults to `email` when omitted. Supply the
            matching channel when the initial content is for `sms` or `push`.
        message:
          type: object
          nullable: true
          description: |
            Optional initial message. When present it initialises the
            server-provisioned default first message and default content.
          properties:
            from:
              type: string
              nullable: true
              description: |
                Sender email address (email channel). Must match an existing
                verified sender for the project, otherwise `422`.
            reply_to:
              type: string
              nullable: true
            content:
              type: object
              properties:
                subject:
                  type: string
                  nullable: true
                preheader_text:
                  type: string
                  nullable: true
                name:
                  type: string
                  nullable: true
                body:
                  "$ref": "#/components/schemas/BroadcastContentBody"
                  description: |
                    Initial content body. Exactly one key valid for the channel
                    (`mjml`/`html`/`text` for email, `text` for sms, `payload`
                    for push); a mismatch returns `422`.
              additionalProperties: false
          additionalProperties: false
        labels:
          type: array
          items:
            type: string
          description: |
            Labels to apply to the new broadcast. Each label is stored lowercased,
            must be non-blank, must not contain a comma, and must be at most 255
            characters once lowercased.
      additionalProperties: false
    UpdateBroadcastRequest:
      type: object
      description: |
        Update message/content-adjacent broadcast metadata only. `channel` may
        change only while the broadcast is a single-message draft whose current
        message is not yet sendable; that switch resets the message content body.
        Audience, segment, trigger, schedule, `send`, launch, lifecycle, and run
        fields are rejected with `422 Validation error`.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Broadcast name. Trimmed before validation; must be non-blank.
        channel:
          type: string
          enum:
            - email
            - sms
            - push
        labels:
          type: array
          items:
            type: string
          description: |
            Replaces the broadcast's labels with this list. Each label is stored
            lowercased, must be non-blank, must not contain a comma, and must be at
            most 255 characters once lowercased.
      additionalProperties: false
    CreateBroadcastMessageRequest:
      type: object
      description: |
        Create an additional message (A/B arm) on a draft broadcast. The arm is
        cloned from the control message's default content (subject, body, sender);
        supplied fields override the clone. `name`, `position`, and other
        derived/audience fields are rejected with `422 Validation error`.
      properties:
        from:
          type: string
          nullable: true
          description: |
            Sender email address (email channel). Must match an existing verified
            sender for the project, otherwise `422`. Defaults to the cloned control
            sender.
        reply_to:
          type: string
          nullable: true
        content:
          type: object
          description: Optional overrides for the cloned default content.
          properties:
            subject:
              type: string
              nullable: true
            preheader_text:
              type: string
              nullable: true
            name:
              type: string
              nullable: true
            body:
              "$ref": "#/components/schemas/BroadcastContentBody"
              description: |
                Override content body. Exactly one key valid for the channel
                (`mjml`/`html`/`text` for email, `text` for sms, `payload` for
                push); a mismatch returns `422`.
          additionalProperties: false
      additionalProperties: false
    UpdateBroadcastMessageRequest:
      type: object
      description: |
        Update a message's sender (`from`/`reply_to`, applied across every one of
        the message's contents) and `active` flag on a draft broadcast. The control
        and the last remaining active message cannot be deactivated (`409`).
        `name`, `position`, and `test_name` are derived and rejected with `422`.
      properties:
        from:
          type: string
          nullable: true
          description: Sender email address (email channel only). Must be a verified sender.
        reply_to:
          type: string
          nullable: true
        active:
          type: boolean
      additionalProperties: false
    CreateBroadcastContentRequest:
      type: object
      description: |
        Create localized content by cloning the message's default content. `locale`
        is required; omitted metadata inherits from the cloned default.
      required:
        - locale
      properties:
        locale:
          type: string
          description: BCP-47-style locale tag for the new localized content.
        name:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        content:
          "$ref": "#/components/schemas/BroadcastContentBody"
          description: |
            Optional content body override. Exactly one key valid for the channel;
            omit to clone the default's body.
      additionalProperties: false
    UpdateBroadcastContentRequest:
      type: object
      description: |
        Partially update broadcast message content. The default content cannot gain
        a locale and localized content cannot clear it; duplicate locales return
        `409`.
      properties:
        locale:
          type: string
        name:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        content:
          "$ref": "#/components/schemas/BroadcastContentBody"
      additionalProperties: false
    Campaign:
      type: object
      required:
        - id
        - title
        - status
        - channel
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        title:
          type: string
        status:
          type: string
          description: Campaign status. Examples include `draft` and `scheduled`.
        channel:
          type: string
          description: Campaign channel, for example `email`.
        scheduled_at:
          type: string
          format: date-time
          nullable: true
          description: The time the campaign is scheduled to send. Null when no send time is set.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CampaignMessage:
      type: object
      required:
        - id
        - title
        - position
        - active
      properties:
        id:
          type: integer
        title:
          type: string
          description: Server-generated message name, for example `Control` or `Variation A`.
        position:
          type: integer
        active:
          type: boolean
    CampaignContentBody:
      type: object
      description: |
        Exactly one of `mjml`, `html`, `text`, or `payload` must be present in write requests.
        Email messages accept `mjml`, `html`, or `text`. SMS accepts `text`. Push accepts `payload`.
      properties:
        mjml:
          type: string
          nullable: true
        html:
          type: string
          nullable: true
        text:
          type: string
          nullable: true
        payload:
          type: object
          nullable: true
          additionalProperties: true
    CampaignContentListItem:
      type: object
      description: List response item; body copy is omitted — fetch a single record for `content`.
      required:
        - id
        - locale
        - name
        - is_default
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        locale:
          type: string
          nullable: true
          description: "`null` for the default content variant."
        name:
          type: string
          nullable: true
        is_default:
          type: boolean
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CampaignContent:
      allOf:
        - "$ref": "#/components/schemas/CampaignContentListItem"
        - type: object
          required:
            - content
          properties:
            content:
              "$ref": "#/components/schemas/CampaignContentBody"
              description: "Full body: exactly one of `mjml`, `html`, `text`, or `payload` is set.

                "
    CreateCampaignContentRequest:
      type: object
      required:
        - locale
      properties:
        locale:
          type: string
          description: Locale for the cloned localized content, for example `fr-FR`.
        name:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        content:
          "$ref": "#/components/schemas/CampaignContentBody"
      additionalProperties: false
    UpdateCampaignContentRequest:
      type: object
      properties:
        name:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        preheader_text:
          type: string
          nullable: true
        locale:
          type: string
          nullable: true
          description:
            Localized rows may update locale; the default content variant cannot be localized in
            place.
        content:
          "$ref": "#/components/schemas/CampaignContentBody"
      additionalProperties: false
    Topic:
      type: object
      description: A preference center subscription category. Topics cannot be deleted at this time.
      required:
        - id
        - name
        - default_status
        - created_at
        - updated_at
      properties:
        id:
          type: integer
        name:
          type: string
          description: Topic name. Must be unique per project, ignoring case. Trimmed before save.
        description:
          type: string
          maxLength: 200
          description: Optional description. Omitted from the response when blank.
        default_status:
          type: string
          enum:
            - subscribed
            - unsubscribed
          description: Default subscription status for profiles with no explicit choice.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TopicListResponse:
      type: object
      description: Cursor-paginated list of topics. No total count is exposed.
      required:
        - data
        - has_more
        - next_cursor
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Topic"
        has_more:
          type: boolean
          description: Whether more results exist beyond this page.
        next_cursor:
          type: string
          nullable: true
          description: Cursor for the next page, or `null` when `has_more` is false.
    CreateTopicRequest:
      type: object
      description: Create a preference center topic. Topics cannot be deleted at this time.
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Topic name. Unique per project, ignoring case. Trimmed before save.
        description:
          type: string
          maxLength: 200
          description: Optional description, at most 200 characters.
        default_status:
          type: string
          enum:
            - subscribed
            - unsubscribed
          default: subscribed
          description:
            Default subscription status for profiles with no explicit choice. Defaults to
            subscribed.
      additionalProperties: false
    UpdateTopicRequest:
      type: object
      minProperties: 1
      description: |
        Update a topic's `name`, `description` and/or `default_status`. Omitted fields are left
        unchanged. Pass `description` as `null` to clear it. At least one field is required.
        Topics cannot be deleted at this time.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: New name. Must be unique per project. Is case-insensitive.
        description:
          type: string
          nullable: true
          maxLength: 200
          description: New description, at most 200 characters. Pass null to clear it.
        default_status:
          type: string
          enum:
            - subscribed
            - unsubscribed
          description: Default subscription status for profiles with no explicit choice.
      additionalProperties: false
    NotFoundError:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          enum:
            - not_found
    ErrorList:
      type: object
      required:
        - errors
      properties:
        errors:
          type: array
          items:
            type: object
            required:
              - message
            properties:
              message:
                type: string
