openapi: 3.1.0
info:
  title: Beecasts Customer API
  version: 1.0.0
  x-last-updated: 2026-09-26
  description: Customer API for connecting WhatsApp sessions, managing contacts
    and shared conversations, sending messages, building inbound-message and
    customer-event workflows, creating broadcasts, and receiving webhook events.
servers:
  - url: https://api.beecasts.com
    description: Production API
security:
  - apiAuthorization: []
tags:
  - name: Sessions
    description: Connect and manage WhatsApp sessions.
  - name: Contacts
    description: Create, import, and manage workspace contacts.
  - name: Messages
    description: Send and manage individual messages and drafts.
  - name: Inbox
    description: Read, assign, resolve, and reply to shared workspace conversations.
  - name: Automations
    description: Build, publish, and monitor durable workflows that respond to
      inbound messages and customer events.
  - name: Media
    description: Upload and retrieve message media.
  - name: Broadcasts
    description: Create, schedule, and manage message broadcasts.
  - name: Webhooks
    description: Configure webhook endpoints and delivery attempts.
paths:
  /v1/sessions:
    get:
      x-api-key-scope: sessions:read
      operationId: listSessions
      tags:
        - Sessions
      summary: List sessions
      x-codeSamples:
        - lang: shell
          label: List sessions
          source: |-
            curl "$BEECASTS_API/v1/sessions" \
              -H "Authorization: Bearer $BEECASTS_API_KEY"
      responses:
        "200":
          description: WhatsApp sessions
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Session"
    post:
      x-api-key-scope: sessions:write
      operationId: createSession
      tags:
        - Sessions
      summary: Create session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                number:
                  type: string
                  description: Optional. The session takes its number from WhatsApp when its QR
                    code is scanned. When given, only that WhatsApp number can
                    pair the session.
      responses:
        "201":
          description: Session created and pairing requested. The response can include a
            QR code while pairing is pending.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Session"
  /v1/sessions/{id}/pairing:
    get:
      x-api-key-scope: sessions:read
      operationId: getSessionPairing
      tags:
        - Sessions
      summary: Get the session pairing QR code
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Session pairing state and QR code, when available.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Session"
    post:
      x-api-key-scope: sessions:write
      operationId: startSessionPairing
      tags:
        - Sessions
      summary: Start QR pairing for a disconnected session
      description: Starts a fresh WhatsApp QR pairing for an unlinked session.
        Disconnecting a session logs it out from WhatsApp, so reconnecting
        requires scanning a new QR code.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Pairing QR code for the existing session.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Session"
        "409":
          description: Session is still linked to a WhatsApp device.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "502":
          description: WhatsApp pairing could not be started.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/sessions/{id}:
    patch:
      x-api-key-scope: sessions:write
      operationId: updateSession
      tags:
        - Sessions
      summary: Update session
      description: Setting status to disconnected logs the linked WhatsApp device out
        and clears its local credentials. Reconnecting that session requires a
        fresh QR pairing via POST /v1/sessions/{id}/pairing.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              minProperties: 1
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                status:
                  type: string
                  enum:
                    - connected
                    - reconnecting
                    - disconnected
                warmupDisabled:
                  type: boolean
                  description: Skip broadcast warm-up for a number that already has a sending
                    history.
      responses:
        "200":
          description: Session updated
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Session"
    delete:
      x-api-key-scope: sessions:write
      operationId: deleteSession
      tags:
        - Sessions
      summary: Delete session
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Session deleted
  /v1/contacts:
    get:
      x-api-key-scope: contacts:read
      operationId: listContacts
      tags:
        - Contacts
      summary: List contacts
      description: Returns a bounded page of workspace contacts. Use page and pageSize
        (maximum 100) for larger workspaces.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Workspace contacts
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/ContactPage"
    post:
      x-api-key-scope: contacts:write
      operationId: createContact
      tags:
        - Contacts
      summary: Create contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - number
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 160
                number:
                  type: string
                  description: Phone number; stored as E.164 digits without a plus.
                country:
                  type: string
                  pattern: ^[A-Za-z]{2}$
                  description: ISO 3166-1 alpha-2 country that reads numbers typed without a
                    country code (for example ID reads 0812… as 62812…). When
                    omitted, numbers must be international, except that a number
                    starting with a single 0 is read in the workspace's default
                    country.
                tags:
                  type: array
                  items:
                    type: string
                lists:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Contact created
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Contact"
  /v1/contacts/import:
    post:
      x-api-key-scope: contacts:write
      operationId: importContacts
      tags:
        - Contacts
      summary: Import contacts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - numbers
              properties:
                numbers:
                  type: array
                  minItems: 1
                  maxItems: 1000
                  items:
                    type: string
                country:
                  type: string
                  pattern: ^[A-Za-z]{2}$
                  description: ISO 3166-1 alpha-2 country that reads numbers typed without a
                    country code (for example ID reads 0812… as 62812…). When
                    omitted, numbers must be international, except that a number
                    starting with a single 0 is read in the workspace's default
                    country.
      responses:
        "200":
          description: Import totals and contacts created or matched by the import.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/ContactImportResult"
  /v1/contacts/opt-outs:
    get:
      x-api-key-scope: contacts:read
      operationId: listOptOuts
      tags:
        - Contacts
      summary: List opted-out numbers
      description: Numbers that replied STOP, BERHENTI or UNSUBSCRIBE, or were added
        by hand. Broadcasts skip them; replying START or MULAI opts back in.
        Requires the `contacts:read` API-key scope.
      responses:
        "200":
          description: Opted-out numbers, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      required:
                        - number
                        - source
                        - keyword
                        - name
                        - createdAt
                      properties:
                        number:
                          type: string
                        source:
                          type: string
                          enum:
                            - keyword
                            - manual
                        keyword:
                          type: string
                        name:
                          type: string
                          description: The matching contact's name
                          if any.: null
                        createdAt:
                          type: integer
    post:
      x-api-key-scope: contacts:write
      operationId: addOptOut
      tags:
        - Contacts
      summary: Opt a number out of broadcasts
      description: Requires the `contacts:write` API-key scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - number
              properties:
                number:
                  type: string
      responses:
        "204":
          description: Opted out
  /v1/contacts/opt-outs/{number}:
    delete:
      x-api-key-scope: contacts:write
      operationId: removeOptOut
      tags:
        - Contacts
      summary: Let broadcasts reach a number again
      description: Requires the `contacts:write` API-key scope.
      parameters:
        - name: number
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Removed
  /v1/contacts/{id}:
    get:
      x-api-key-scope: contacts:read
      operationId: getContact
      tags:
        - Contacts
      summary: Get contact
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Contact details
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Contact"
        "404":
          description: Contact not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    patch:
      x-api-key-scope: contacts:write
      operationId: updateContact
      tags:
        - Contacts
      summary: Update contact
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - number
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 160
                number:
                  type: string
                  description: Phone number; stored as E.164 digits without a plus.
                country:
                  type: string
                  pattern: ^[A-Za-z]{2}$
                  description: ISO 3166-1 alpha-2 country that reads numbers typed without a
                    country code (for example ID reads 0812… as 62812…). When
                    omitted, numbers must be international, except that a number
                    starting with a single 0 is read in the workspace's default
                    country.
                tags:
                  type: array
                  items:
                    type: string
                lists:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Contact updated
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Contact"
    delete:
      x-api-key-scope: contacts:write
      operationId: deleteContact
      tags:
        - Contacts
      summary: Delete contact
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Contact deleted
  /v1/messages:
    get:
      x-api-key-scope: messages:read
      operationId: listMessages
      tags:
        - Messages
      summary: List messages
      description: Returns a bounded page of workspace messages. Use page and pageSize
        (maximum 100) for larger workspaces. contactId limits history to a
        workspace contact.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: contactId
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Workspace message history
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/MessagePage"
  /v1/messages/send:
    post:
      x-api-key-scope: messages:write
      operationId: sendMessage
      tags:
        - Messages
      summary: Send a message to a phone number
      description: Queues a text or uploaded media message through a linked WhatsApp
        device. Sending requires a connected workspace session and remaining
        allowance on the effective plan. A saved contact is not required;
        contactId is optional and can associate a matching workspace contact for
        context. Free allows 500 inbound, outbound, and broadcast messages per
        WIB calendar month; it adds an italic footer to outbound text and
        captions. Free image, video, and document messages need a caption;
        audio-only messages require a paid plan. A queued attempt consumes one
        allowance even if delivery later fails.
      requestBody:
        required: true
        content:
          application/json:
            examples:
              numberOnlyRecipient:
                summary: Send without a saved Contact
                value:
                  sessionId: ses_example
                  recipient: REPLACE_WITH_RECIPIENT_PHONE
                  type: text
                  body: Hello
            schema:
              type: object
              required:
                - sessionId
                - recipient
                - type
              properties:
                sessionId:
                  type: string
                contactId:
                  type: string
                  description: Optional workspace contact association; when provided
                  it must match the recipient number.: null
                recipient:
                  type: string
                  description: "Recipient phone number: international (+6281…, 006281… or 6281…)
                    or, with country, national (0812…). Spaces, hyphens,
                    periods, parentheses and a leading plus are allowed. Stored
                    and sent as E.164 digits without a plus."
                country:
                  type: string
                  pattern: ^[A-Za-z]{2}$
                  description: ISO 3166-1 alpha-2 country that reads numbers typed without a
                    country code (for example ID reads 0812… as 62812…). When
                    omitted, numbers must be international, except that a number
                    starting with a single 0 is read in the workspace's default
                    country.
                type:
                  type: string
                  enum:
                    - text
                    - image
                    - video
                    - audio
                    - document
                body:
                  type: string
                  description: Required for text and for Free image/video/document captions; empty
                    only for paid audio. Variables such as {{name}},
                    {{first_name}}, {{phone}} and contact custom fields are
                    filled on the server from the contact whose number matches
                    the recipient; {{name|there}} supplies a fallback. A
                    variable with no value and no fallback is removed, so raw
                    {{...}} is never sent.
                mediaUrl:
                  type: string
                  description: Workspace media URL returned by uploadMedia; required for media
                    messages
                variables:
                  type: object
                  maxProperties: 32
                  description: Optional values for {{key}} variables in the body; they take
                    precedence over contact data. Keys start with a letter
                    (letters, digits, underscores, up to 32 characters); values
                    are up to 1024 bytes.
                  propertyNames:
                    pattern: ^[A-Za-z][A-Za-z0-9_]{0,31}$
                  additionalProperties:
                    type: string
                    maxLength: 1024
      responses:
        "202":
          description: Message queued for delivery to the recipient number.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Message"
        "422":
          description: Invalid message or Free-plan validation failed. Stable codes
            include free_caption_required, free_audio_unavailable, and
            message_body_too_long; recipient_blocked when the number is on the
            Beecasts abuse blocklist (the message is not queued), and
            content_blocked when the text (after variables are filled) contains
            a blocked keyword.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: The current message allowance is exhausted (code quota_exceeded).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/messages/{id}/retry:
    post:
      x-api-key-scope: messages:write
      operationId: retryMessage
      tags:
        - Messages
      summary: Retry a failed message
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "202":
          description: Failed message retry queued.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Message"
        "422":
          description: Free message validation failed. Stable codes include
            free_caption_required, free_audio_unavailable, and
            message_body_too_long.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: The current message allowance is exhausted (code quota_exceeded).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/messages/{id}:
    get:
      x-api-key-scope: messages:read
      operationId: getMessage
      tags:
        - Messages
      summary: Get message
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Workspace-scoped message detail
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Message"
        "404":
          description: Message was not found in the active workspace
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    delete:
      x-api-key-scope: messages:write
      operationId: deleteMessage
      tags:
        - Messages
      summary: Delete message
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Message deleted
  /v1/media:
    post:
      x-api-key-scope: messages:write
      operationId: uploadMedia
      tags:
        - Media
      summary: Upload media
      description: Stores a workspace media file, maximum 16 MB, for linked-device sending.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "201":
          description: Media uploaded; use the returned workspace URL in a message or
            broadcast.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/MediaUpload"
  /v1/media/{id}:
    get:
      x-api-key-scope: messages:read
      operationId: getMedia
      tags:
        - Media
      summary: Get media
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Workspace media file. The response body is the raw file bytes, not
            a JSON envelope.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
  /v1/message-drafts:
    get:
      x-api-key-scope: messages:read
      operationId: listMessageDrafts
      tags:
        - Messages
      summary: List message drafts
      description: Lists reusable linked-device drafts. These are not Meta-approved
        templates.
      responses:
        "200":
          description: Workspace message drafts
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/MessageDraft"
    post:
      x-api-key-scope: messages:write
      operationId: createMessageDraft
      tags:
        - Messages
      summary: Create message draft
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageDraftInput"
      responses:
        "201":
          description: Draft created
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/MessageDraft"
  /v1/message-drafts/{id}:
    put:
      x-api-key-scope: messages:write
      operationId: updateMessageDraft
      tags:
        - Messages
      summary: Update message draft
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageDraftInput"
      responses:
        "200":
          description: Draft updated
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/MessageDraft"
    delete:
      x-api-key-scope: messages:write
      operationId: deleteMessageDraft
      tags:
        - Messages
      summary: Delete message draft
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Draft deleted
  /v1/inbox:
    get:
      x-api-key-scope: inbox:read
      operationId: listInboxConversations
      tags:
        - Inbox
      summary: List shared inbox conversations
      description: Returns workspace-scoped conversations that have received at least
        one inbound WhatsApp message. Outbound messages in those threads remain
        visible for context. A saved contact is optional.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: status
          in: query
          schema:
            type: string
            enum:
              - all
              - open
              - resolved
            default: all
        - name: sessionId
          in: query
          schema:
            type: string
        - name: assigneeId
          in: query
          description: Use unassigned to filter conversations without an assignee.
          schema:
            type: string
        - name: label
          in: query
          schema:
            type: string
        - name: awaiting
          in: query
          description: true keeps only open conversations waiting on a reply.
          schema:
            type: boolean
        - name: search
          in: query
          schema:
            type: string
            maxLength: 120
      responses:
        "200":
          description: Paginated inbox conversations.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxPage"
  /v1/inbox/assignees:
    get:
      x-api-key-scope: inbox:read
      operationId: listInboxAssignees
      tags:
        - Inbox
      summary: List active workspace members who can be assigned conversations
      responses:
        "200":
          description: Active members in the current workspace.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/InboxAssignee"
  /v1/inbox/labels:
    get:
      x-api-key-scope: inbox:read
      operationId: listInboxLabels
      tags:
        - Inbox
      summary: List labels used in the inbox
      description: Requires the `inbox:read` API-key scope.
      responses:
        "200":
          description: Labels in alphabetical order
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      type: string
  /v1/inbox/quick-replies:
    get:
      x-api-key-scope: inbox:read
      operationId: listInboxQuickReplies
      tags:
        - Inbox
      summary: List quick replies
      description: Requires the `inbox:read` API-key scope.
      responses:
        "200":
          description: Quick replies by shortcut
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/InboxQuickReply"
    post:
      x-api-key-scope: inbox:write
      operationId: createInboxQuickReply
      tags:
        - Inbox
      summary: Create a quick reply
      description: Requires the `inbox:write` API-key scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - shortcut
                - body
              properties:
                shortcut:
                  type: string
                  pattern: ^/?[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$
                body:
                  type: string
                  maxLength: 4096
      responses:
        "201":
          description: Quick reply
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxQuickReply"
  /v1/inbox/quick-replies/{replyId}:
    put:
      x-api-key-scope: inbox:write
      operationId: updateInboxQuickReply
      tags:
        - Inbox
      summary: Update a quick reply
      description: Requires the `inbox:write` API-key scope.
      parameters:
        - name: replyId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - shortcut
                - body
              properties:
                shortcut:
                  type: string
                  pattern: ^/?[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$
                body:
                  type: string
                  maxLength: 4096
      responses:
        "200":
          description: Quick reply
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxQuickReply"
    delete:
      x-api-key-scope: inbox:write
      operationId: deleteInboxQuickReply
      tags:
        - Inbox
      summary: Delete a quick reply
      description: Requires the `inbox:write` API-key scope.
      parameters:
        - name: replyId
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Deleted
  /v1/inbox/{id}/labels:
    put:
      x-api-key-scope: inbox:write
      operationId: setInboxLabels
      tags:
        - Inbox
      summary: Set a conversation's labels
      description: Replaces all labels. Up to 10, 32 characters each; they're
        lowercased. Requires the `inbox:write` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - labels
              properties:
                labels:
                  type: array
                  maxItems: 10
                  items:
                    type: string
                    maxLength: 32
      responses:
        "200":
          description: Saved labels
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - labels
                    properties:
                      labels:
                        type: array
                        items:
                          type: string
  /v1/inbox/{id}/notes:
    post:
      x-api-key-scope: inbox:write
      operationId: addInboxNote
      tags:
        - Inbox
      summary: Add an internal note
      description: Visible to the team only; it's never sent to the customer. Requires
        a signed-in user session.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  type: string
                  maxLength: 4000
      responses:
        "201":
          description: Note
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxNote"
  /v1/inbox/{id}/notes/{noteId}:
    delete:
      x-api-key-scope: inbox:write
      operationId: deleteInboxNote
      tags:
        - Inbox
      summary: Delete an internal note
      description: Only the author or an owner or admin can delete a note. Requires
        the `inbox:write` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Deleted
  /v1/inbox/{id}:
    get:
      x-api-key-scope: inbox:read
      operationId: getInboxConversation
      tags:
        - Inbox
      summary: Read a conversation and recent messages
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Conversation context and up to 200 chronological messages.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxDetail"
        "404":
          description: Conversation was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/inbox/{id}/assignee:
    patch:
      x-api-key-scope: inbox:write
      operationId: assignInboxConversation
      tags:
        - Inbox
      summary: Assign or unassign a conversation
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - assigneeId
              properties:
                assigneeId:
                  type: string
                  description: Active workspace member ID; send an empty string to unassign.
      responses:
        "200":
          description: Updated conversation.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxDetail"
        "422":
          description: Assignee is not an active workspace member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/inbox/{id}/status:
    patch:
      x-api-key-scope: inbox:write
      operationId: setInboxConversationStatus
      tags:
        - Inbox
      summary: Resolve or reopen a conversation
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - status
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - resolved
      responses:
        "200":
          description: Updated conversation.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/InboxDetail"
  /v1/inbox/{id}/reply:
    post:
      x-api-key-scope: inbox:write
      operationId: replyToInboxConversation
      tags:
        - Inbox
      summary: Queue a text or media reply
      description: Queues a WhatsApp message through the conversation session. Send
        text in `body`, or attach a file uploaded with `POST /v1/media` through
        `mediaId`; `body` is then the optional caption (audio takes no caption).
        Plan quota and Free-plan branding rules apply, so Free-plan media needs
        a caption. No saved contact is required.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  maxLength: 4096
                  description: Reply text
                  or the caption when `mediaId` is set. Required when there is no `mediaId`.: null
                mediaId:
                  type: string
                  description: ID returned by `POST /v1/media`. The message type follows the file
                    (image
                  video: null
                  audio or document).: null
      responses:
        "202":
          description: Reply accepted by the message queue.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Message"
        "409":
          description: The conversation is resolved and must be reopened before replying.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: The workspace message allowance is exhausted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations:
    get:
      x-api-key-scope: automations:read
      operationId: listAutomations
      tags:
        - Automations
      summary: List automation rules
      description: Requires the `automations:read` API-key scope.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: archived
          in: query
          description: Set true to list archived rules and their run history instead of
            active rules.
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: Paginated automation rules.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRulePage"
    post:
      x-api-key-scope: automations:write
      operationId: createAutomation
      tags:
        - Automations
      summary: Create an inactive inbound reply rule
      description: Requires the `automations:write` API-key scope. The only supported
        trigger is an inbound WhatsApp message. If conditionContains is set, the
        message text must contain it ignoring case. Matching messages queue one
        text reply. New rules start inactive.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - replyText
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 128
                conditionContains:
                  type: string
                  maxLength: 256
                replyText:
                  type: string
                  minLength: 1
                  maxLength: 4096
      responses:
        "201":
          description: Inactive rule created.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "422":
          description: Rule fields are invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}:
    get:
      x-api-key-scope: automations:read
      operationId: getAutomation
      tags:
        - Automations
      summary: Get an automation rule
      description: Requires the `automations:read` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Automation rule details.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "404":
          description: Rule was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    patch:
      x-api-key-scope: automations:write
      operationId: updateAutomation
      tags:
        - Automations
      summary: Update or activate an automation rule
      description: Requires the `automations:write` API-key scope. Send the complete
        current rule fields. Transitioning an inactive rule to enabled requires
        confirmActivation=true because it can send real WhatsApp messages.
        Disabling cancels pending runs; a run already claimed by a worker may
        finish.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - replyText
                - enabled
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 128
                conditionContains:
                  type: string
                  maxLength: 256
                replyText:
                  type: string
                  minLength: 1
                  maxLength: 4096
                enabled:
                  type: boolean
                confirmActivation:
                  type: boolean
                  description: Required when enabling a currently inactive rule.
      responses:
        "200":
          description: Updated automation rule.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "409":
          description: Explicit activation confirmation is required.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    delete:
      x-api-key-scope: automations:write
      operationId: archiveAutomation
      tags:
        - Automations
      summary: Archive and disable an automation rule
      description: Requires the `automations:write` API-key scope. Pending runs are
        cancelled and existing run history is retained.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Rule archived and pending runs cancelled; history is retained.
  /v1/automations/{id}/runs:
    get:
      x-api-key-scope: automations:read
      operationId: listAutomationRuns
      tags:
        - Automations
      summary: List an automation's execution history
      description: Requires the `automations:read` API-key scope. Trigger data is
        omitted from run summaries.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Durable runs are retried with backoff and marked failed after eight
            unsuccessful attempts.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRunPage"
  /v1/automation-runs/{runId}/retry:
    post:
      x-api-key-scope: automations:write
      operationId: retryAutomationRun
      tags:
        - Automations
      summary: Retry a terminally failed automation run
      description: Requires the `automations:write` API-key scope. Only the failed
        step is retried, and the automation must be active.
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
      responses:
        "202":
          description: The failed run was queued for another attempt.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - id
                      - status
                    properties:
                      id:
                        type: string
                      status:
                        type: string
                        enum:
                          - pending
        "409":
          description: Only failed runs for an active automation can be retried.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automation-flows:
    post:
      x-api-key-scope: automations:write
      operationId: createAutomationFlow
      tags:
        - Automations
      summary: Create an inactive visual automation flow
      description: Requires the `automations:write` API-key scope. New flows start
        inactive. Publishing a flow that can send text may deliver messages to
        real WhatsApp recipients and uses the normal workspace quota and
        Free-plan branding rules.
      x-codeSamples:
        - lang: shell
          label: Create an order confirmation flow
          source: >-
            curl -X POST "$BEECASTS_API/v1/automation-flows" \
              -H "Authorization: Bearer $BEECASTS_API_KEY" \
              -H "Content-Type: application/json" \
              --data '{
                "name": "Order confirmations",
                "graph": {
                  "trigger": { "type": "custom_event", "eventName": "orders.created" },
                  "entryNodeId": "send_confirmation",
                  "nodes": [
                    {
                      "id": "send_confirmation",
                      "type": "send_text",
                      "sessionId": "ses_example",
                      "recipientField": "event.customerPhone",
                      "body": "We received order {{event.orderId}}.",
                      "next": "finish"
                    },
                    { "id": "finish", "type": "end" }
                  ]
                }
              }'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAutomationFlowRequest"
      responses:
        "201":
          description: Inactive flow and its first draft.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "422":
          description: The flow name or graph is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/draft:
    get:
      x-api-key-scope: automations:read
      operationId: getAutomationDraft
      tags:
        - Automations
      summary: Read the current editable workflow draft
      description: Requires the `automations:read` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Revisioned draft graph.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationDraft"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    put:
      x-api-key-scope: automations:write
      operationId: saveAutomationDraft
      tags:
        - Automations
      summary: Save a validated workflow draft
      description: Requires the `automations:write` API-key scope. Saving increments
        the revision and invalidates any prior test result. Use the current
        revision to avoid overwriting another editor's changes.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SaveAutomationDraftRequest"
      responses:
        "200":
          description: Saved workflow draft with the incremented revision.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationDraft"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: The draft revision is stale.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: The workflow graph is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/validate:
    post:
      x-api-key-scope: automations:read
      operationId: validateAutomationDraft
      tags:
        - Automations
      summary: Validate the current workflow draft
      description: Requires the `automations:read` API-key scope. This POST is
        read-only and does not save or publish the draft.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Validation result and field-specific issues.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WorkflowValidationResult"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/test-runs:
    post:
      x-api-key-scope: automations:write
      operationId: testAutomationDraft
      tags:
        - Automations
      summary: Run a no-send simulation of the current draft
      description: Requires the `automations:write` API-key scope. Simulation never
        sends a message. A passing test authorizes publishing only while the
        draft revision and graph remain unchanged.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TestAutomationDraftRequest"
      responses:
        "200":
          description: No-send simulation and field-level results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WorkflowTestResult"
        "400":
          description: The sample trigger data is not a JSON object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/versions:
    get:
      x-api-key-scope: automations:read
      operationId: listAutomationVersions
      tags:
        - Automations
      summary: List immutable published workflow versions
      description: Requires the `automations:read` API-key scope. Each active run
        remains pinned to the version that started it.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Published versions in descending version order.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - items
                    properties:
                      items:
                        type: array
                        items:
                          $ref: "#/components/schemas/AutomationVersion"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/publish:
    post:
      x-api-key-scope: automations:write
      operationId: publishAutomationDraft
      tags:
        - Automations
      summary: Publish the tested workflow draft
      description: Requires the `automations:write` API-key scope. The exact current
        revision must have passed a no-send test and confirmSend must be true.
        Publishing activates the new version; send steps may deliver WhatsApp
        messages to real recipients, use quota, and apply Free-plan branding.
        Existing runs keep their pinned version, and publishing does not resume
        a paused flow.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishAutomationDraftRequest"
      responses:
        "200":
          description: Newly published immutable version.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationVersion"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: The revision is stale
          test is missing: null
          or send confirmation is required.: null
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: The workflow graph is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/pause:
    post:
      x-api-key-scope: automations:write
      operationId: pauseAutomation
      tags:
        - Automations
      summary: Pause a published automation
      description: Requires the `automations:write` API-key scope. Pausing stops new
        triggers and parks pending or waiting runs. No paused trigger is
        backfilled when the flow resumes.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Updated automation state.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automations/{id}/resume:
    post:
      x-api-key-scope: automations:write
      operationId: resumeAutomation
      tags:
        - Automations
      summary: Resume a paused published automation
      description: Requires the `automations:write` API-key scope. Resuming continues
        parked runs with their remaining wait time and does not replay triggers
        received while paused. Publish a tested version before resuming an
        unpublished flow.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Updated automation state.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRule"
        "404":
          description: Flow was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Publish a workflow before resuming it.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/knowledge:
    get:
      x-api-key-scope: automations:read
      operationId: listKnowledge
      tags:
        - Automations
      summary: List the knowledge base
      description: The FAQs and notes that AI reply steps answer from, oldest first,
        with how much of the size limit they use. Requires the
        `automations:read` API-key scope.
      responses:
        "200":
          description: Knowledge base entries.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/KnowledgeBase"
    post:
      x-api-key-scope: automations:write
      operationId: createKnowledgeEntry
      tags:
        - Automations
      summary: Add a knowledge base entry
      description: The knowledge base is limited to 100,000 characters and 500
        entries, or 2,000,000 characters and 5,000 entries when the server has
        an embeddings provider. Requires the `automations:write` API-key scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeEntryInput"
      responses:
        "201":
          description: Entry added.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/KnowledgeEntry"
        "409":
          description: The knowledge base is full (knowledge_base_full).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: Invalid kind
          title or content.: null
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/knowledge/import:
    post:
      x-api-key-scope: automations:write
      operationId: importKnowledgeFile
      tags:
        - Automations
      summary: Import a file into the knowledge base
      description: Extracts the text of a PDF (with selectable text), Word .docx,
        .txt, .md or .csv file of up to 10 MB and adds it as notes titled with
        the file name, split into parts of up to 20,000 characters. Scanned PDFs
        without a text layer are rejected with no_text_found. Requires the
        `automations:write` API-key scope.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "201":
          description: Entries added from the file.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - items
                    properties:
                      items:
                        type: array
                        items:
                          $ref: "#/components/schemas/KnowledgeEntry"
        "409":
          description: The file's text would exceed the knowledge base size limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "413":
          description: The file is larger than 10 MB.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: Unsupported file type or no readable text.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/knowledge/{id}:
    put:
      x-api-key-scope: automations:write
      operationId: updateKnowledgeEntry
      tags:
        - Automations
      summary: Replace a knowledge base entry
      description: Requires the `automations:write` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeEntryInput"
      responses:
        "200":
          description: Entry updated.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/KnowledgeEntry"
        "404":
          description: Entry not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: The change would exceed the knowledge base size limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
    delete:
      x-api-key-scope: automations:write
      operationId: deleteKnowledgeEntry
      tags:
        - Automations
      summary: Delete a knowledge base entry
      description: Requires the `automations:write` API-key scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Entry deleted.
        "404":
          description: Entry not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automation-events/{eventName}:
    post:
      x-api-key-scope: automations:write
      operationId: submitAutomationEvent
      tags:
        - Automations
      summary: Submit a customer event to matching published workflows
      description: Requires the `automations:write` API-key scope. Supply a JSON
        object no larger than 32 KiB and a stable Idempotency-Key. The same key
        and payload returns its original receipt; reusing a key with a changed
        payload returns 409. Matching send steps can message real WhatsApp
        recipients, use quota, and apply Free-plan branding.
      x-codeSamples:
        - lang: shell
          label: Submit a new order event
          source: |-
            curl -X POST "$BEECASTS_API/v1/automation-events/orders.created" \
              -H "Authorization: Bearer $BEECASTS_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: order-123-confirmation" \
              --data '{"orderId":"ord_123","customerPhone":"+15550123456"}'
      parameters:
        - name: eventName
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z][a-z0-9._-]{0,63}$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 128
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AutomationEventPayload"
              x-maxBytes: 32768
      responses:
        "202":
          description: Event receipt with counts of queued and paused workflows.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/EventAcceptance"
        "400":
          description: Event name
          idempotency key: null
          or JSON body is invalid or too large.: null
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: The idempotency key was already used with a different payload.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/automation-runs/{runId}:
    get:
      x-api-key-scope: automations:read
      operationId: getAutomationRun
      tags:
        - Automations
      summary: Read a workflow run and its durable step history
      description: Requires the `automations:read` API-key scope. Trigger payloads and
        raw provider errors are never returned. Per-step history reports retry
        attempts and safe error details.
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Run summary with ordered workflow step history.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/AutomationRunDetail"
        "404":
          description: Run was not found in this workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/broadcasts:
    get:
      x-api-key-scope: broadcasts:read
      operationId: listBroadcasts
      tags:
        - Broadcasts
      summary: List broadcasts
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: status
          in: query
          schema:
            type: string
            enum:
              - draft
              - scheduled
              - running
              - paused
              - completed
              - cancelled
              - failed
        - name: search
          in: query
          schema:
            type: string
            maxLength: 160
      x-codeSamples:
        - lang: shell
          label: List broadcasts
          source: |-
            curl "$BEECASTS_API/v1/broadcasts?page=1&pageSize=20" \
              -H "Authorization: Bearer $BEECASTS_API_KEY"
      responses:
        "200":
          description: Paginated campaigns
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/CampaignPage"
    post:
      x-api-key-scope: broadcasts:write
      operationId: createBroadcast
      tags:
        - Broadcasts
      summary: Create a broadcast draft
      description: Creates a draft campaign with phone-number recipients. Recipients
        do not need saved Contact records; matching workspace contacts can
        provide personalization fields.
      requestBody:
        required: true
        content:
          application/json:
            examples:
              manualNumbers:
                summary: Create a draft for phone-number recipients
                value:
                  name: Example broadcast
                  sessionId: ses_example
                  message: Replace with a test message
                  speed: 10
                  recipients:
                    - number: REPLACE_WITH_RECIPIENT_PHONE
            schema:
              type: object
              required:
                - name
                - sessionId
                - message
                - speed
                - recipients
              properties:
                name:
                  type: string
                sessionId:
                  type: string
                message:
                  type: string
                messageType:
                  type: string
                  enum:
                    - text
                    - image
                    - video
                    - audio
                    - document
                  default: text
                mediaUrl:
                  type: string
                  description: Workspace media URL for media broadcasts
                speed:
                  type: integer
                  minimum: 1
                  maximum: 240
                country:
                  type: string
                  pattern: ^[A-Za-z]{2}$
                  description: ISO 3166-1 alpha-2 country that reads numbers typed without a
                    country code (for example ID reads 0812… as 62812…). When
                    omitted, numbers must be international, except that a number
                    starting with a single 0 is read in the workspace's default
                    country.
                recipients:
                  type: array
                  minItems: 1
                  maxItems: 1000
                  items:
                    type: object
                    required:
                      - number
                    properties:
                      number:
                        type: string
                        description: Recipient phone number, international or national in country;
                          spaces, hyphens, periods, parentheses and a leading
                          plus are allowed. Stored as E.164 digits without a
                          plus. No saved Contact is required.
                      contactId:
                        type: string
                        description: Optional workspace Contact association; when provided
                        it must match the phone number: null
      responses:
        "201":
          description: Draft campaign created
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
        "400":
          description: Invalid JSON request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "413":
          description: More than 1,000 recipients
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: "Invalid campaign or recipient phone number (invalid_campaign,
            invalid_campaign_recipient), or content_blocked when the message
            contains a keyword on the Beecasts abuse blocklist. Recipients on
            the blocklist are not refused here: when the broadcast sends, each
            is marked failed with the blocked_recipient reason."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/broadcasts/{id}:
    get:
      x-api-key-scope: broadcasts:read
      operationId: getBroadcast
      tags:
        - Broadcasts
      summary: Get broadcast
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign details and a page of recipients.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/CampaignDetail"
    patch:
      x-api-key-scope: broadcasts:write
      operationId: updateBroadcast
      tags:
        - Broadcasts
      summary: Update broadcast
      description: Updates editable draft campaign content and sending speed. A
        matching workspace Contact is optional and used only for
        personalization.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                message:
                  type: string
                messageType:
                  type: string
                  enum:
                    - text
                    - image
                    - video
                    - audio
                    - document
                mediaUrl:
                  type: string
                  description: Workspace media URL for media broadcasts.
                speed:
                  type: integer
                  minimum: 1
                  maximum: 240
      responses:
        "200":
          description: Campaign updated
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/progress:
    get:
      x-api-key-scope: broadcasts:read
      operationId: getBroadcastProgress
      tags:
        - Broadcasts
      summary: Get broadcast progress
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign delivery totals and failure reasons.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/CampaignProgress"
  /v1/broadcasts/{id}/schedule:
    post:
      x-api-key-scope: broadcasts:write
      operationId: scheduleBroadcast
      tags:
        - Broadcasts
      summary: Schedule a broadcast
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scheduledAt
              properties:
                scheduledAt:
                  type: integer
                  description: Scheduled time as Unix milliseconds.
      responses:
        "200":
          description: Campaign scheduled
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/start:
    post:
      x-api-key-scope: broadcasts:write
      operationId: startBroadcast
      tags:
        - Broadcasts
      summary: Start a broadcast and deliver messages
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign started and recipient messages may be delivered.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/pause:
    post:
      x-api-key-scope: broadcasts:write
      operationId: pauseBroadcast
      tags:
        - Broadcasts
      summary: Pause broadcast
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign paused
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/resume:
    post:
      x-api-key-scope: broadcasts:write
      operationId: resumeBroadcast
      tags:
        - Broadcasts
      summary: Resume a paused broadcast and deliver messages
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign resumed; pending recipient messages may be delivered.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/cancel:
    post:
      x-api-key-scope: broadcasts:write
      operationId: cancelBroadcast
      tags:
        - Broadcasts
      summary: Cancel broadcast
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaign cancelled
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/broadcasts/{id}/duplicate:
    post:
      x-api-key-scope: broadcasts:write
      operationId: duplicateBroadcast
      tags:
        - Broadcasts
      summary: Duplicate broadcast
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "201":
          description: Campaign duplicated as a new draft.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/Campaign"
  /v1/webhooks:
    get:
      x-api-key-scope: webhooks:read
      operationId: listWebhooks
      tags:
        - Webhooks
      summary: List webhooks
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: search
          in: query
          schema:
            type: string
            maxLength: 160
        - name: status
          in: query
          schema:
            type: string
            enum:
              - active
              - paused
      responses:
        "200":
          description: Webhook endpoints and delivery summary.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookPage"
    post:
      x-api-key-scope: webhooks:write
      operationId: createWebhook
      tags:
        - Webhooks
      summary: Create webhook
      requestBody:
        required: true
        content:
          application/json:
            examples:
              messageEvents:
                summary: Subscribe to message events
                value:
                  name: Order updates
                  url: https://example.com/webhooks/beecasts
                  events:
                    - message.sent
                    - message.failed
            schema:
              type: object
              required:
                - name
                - url
                - events
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                url:
                  type: string
                  format: uri
                  description: HTTPS endpoint that can receive webhook POST requests.
                events:
                  type: array
                  minItems: 1
                  items:
                    type: string
      responses:
        "201":
          description: Webhook endpoint created and its signing secret returned once.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookSecretResult"
  /v1/webhooks/{id}:
    get:
      x-api-key-scope: webhooks:read
      operationId: getWebhook
      tags:
        - Webhooks
      summary: Get webhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Webhook endpoint status and delivery counts.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookEndpoint"
    delete:
      x-api-key-scope: webhooks:write
      operationId: deleteWebhook
      tags:
        - Webhooks
      summary: Delete webhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Webhook endpoint disabled
  /v1/webhooks/{id}/rotate-secret:
    post:
      x-api-key-scope: webhooks:write
      operationId: rotateWebhookSecret
      tags:
        - Webhooks
      summary: Rotate webhook secret
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Webhook secret rotated; the replacement is returned once.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookSecretResult"
  /v1/webhooks/{id}/test:
    post:
      x-api-key-scope: webhooks:write
      operationId: testWebhook
      tags:
        - Webhooks
      summary: Test webhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "202":
          description: Test delivery queued.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookTestResult"
  /v1/webhooks/{id}/attempts:
    get:
      x-api-key-scope: webhooks:read
      operationId: listWebhookAttempts
      tags:
        - Webhooks
      summary: List webhook attempts
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: search
          in: query
          schema:
            type: string
            maxLength: 160
        - name: status
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Paginated webhook delivery attempts.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookAttemptPage"
  /v1/webhooks/{id}/attempts/{attemptId}/retry:
    post:
      x-api-key-scope: webhooks:write
      operationId: retryWebhookAttempt
      tags:
        - Webhooks
      summary: Retry webhook attempt
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: attemptId
          in: path
          required: true
          schema:
            type: string
      responses:
        "202":
          description: Retry queued
components:
  securitySchemes:
    apiAuthorization:
      type: http
      scheme: bearer
      description: "Send a workspace API key as `Authorization: Bearer bca_live_...`.
        A key only reaches its own workspace, and revoked or expired keys get
        401 unauthorized. Every operation names the scope a key needs in
        `x-api-key-scope`; a key without it gets 403 insufficient_scope with the
        missing scope in `error.requiredScope`. Operations marked `none` are for
        signed-in users only and answer API keys with 403
        interactive_session_required. Scopes: workspace, sessions, contacts,
        messages, inbox, automations, broadcasts, webhooks and api_keys come as
        `:read` and `:write`; billing, team, diagnostics and logs are `:read`
        only. Signed-in users are governed by their team role instead."
  schemas:
    Session:
      type: object
      required:
        - id
        - name
        - number
        - profileName
        - status
        - pairingRequired
        - neverLinked
        - sentToday
        - receivedToday
        - failedToday
        - lastHeartbeatAt
        - version
        - createdAt
        - broadcastDailyLimit
        - warmupEndsAt
        - warmupDisabled
      properties:
        id:
          type: string
        name:
          type: string
        number:
          type: string
          description: The linked WhatsApp number in international digits without a
            leading +, as reported by WhatsApp when the QR code is scanned.
            Empty until the session is paired; it never changes afterwards.
        profileName:
          type: string
        status:
          type: string
          enum:
            - connected
            - reconnecting
            - disconnected
          description: A session that has never had a WhatsApp link (neverLinked) is
            always reported disconnected, never reconnecting, also while its QR
            pairing is open.
        pairingRequired:
          type: boolean
          description: True when the session is disconnected and needs a new WhatsApp QR
            pairing.
        neverLinked:
          type: boolean
          description: "True when the session has never had a WhatsApp link: it is not
            paired yet (new, or its QR pairing was abandoned or rejected). Such
            a session has nothing to reconnect or lose, so show it as not paired
            rather than disconnected; pairingRequired is true. False once it has
            connected, including after it was later logged out or unlinked."
        pairingState:
          type: string
          description: The provider's pairing progress, on pairing responses, for example
            pending (QR code shown), connecting (scanned), connected, or expired
            (start a new pairing).
        qrCode:
          type: string
        pairingExpiresAt:
          type: integer
        pairingError:
          type: string
          description: Why the last QR scan was rejected, for example because the number
            is already used by another session in the workspace. Cleared when
            pairing starts again or succeeds.
        disconnectReason:
          type: string
          enum:
            - logged_out
            - banned
            - temporarily_banned
            - replaced
            - client_outdated
            - connect_failed
            - connection_lost
          description: Set only while the session is disconnected, when the link was lost
            for a known reason and won't come back by itself. logged_out and
            banned mean the device was unlinked, so pairingRequired is true.
            Cleared when the session reconnects or pairs again.
        disconnectMessage:
          type: string
          description: A short explanation of disconnectReason for people, suitable to
            show next to the session.
        sentToday:
          type: integer
          description: Outbound messages created today in the workspace time zone.
        receivedToday:
          type: integer
          description: Inbound messages received today in the workspace time zone.
        failedToday:
          type: integer
          description: Outbound messages created today that failed.
        lastHeartbeatAt:
          type: integer
        version:
          type: string
        createdAt:
          type: integer
        broadcastDailyLimit:
          type: integer
          description: Broadcast messages this number may send today while it warms up; 0
            means no warm-up limit. Replies and one-to-one messages are never
            limited.
        warmupEndsAt:
          type: integer
          description: Unix milliseconds when warm-up ends; 0 when the number isn't
            warming up.
        warmupDisabled:
          type: boolean
          description: True when the number skips warm-up because it already has a sending
            history.
    ContactField:
      type: object
      required:
        - key
        - value
      properties:
        key:
          type: string
        value:
          type: string
    Contact:
      type: object
      required:
        - id
        - name
        - number
        - country
        - tags
        - lists
        - lastActivityAt
        - createdAt
        - customFields
      properties:
        id:
          type: string
        name:
          type: string
        number:
          type: string
        country:
          type: string
        tags:
          type: array
          items:
            type: string
        lists:
          type: array
          items:
            type: string
        lastActivityAt:
          type: integer
        createdAt:
          type: integer
        customFields:
          type: array
          items:
            $ref: "#/components/schemas/ContactField"
    ContactPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Contact"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    ContactImportResult:
      type: object
      required:
        - imported
        - duplicates
        - invalid
        - contacts
      properties:
        imported:
          type: integer
        duplicates:
          type: integer
        invalid:
          type: integer
          description: Numbers skipped because they are not valid phone numbers.
        contacts:
          type: array
          items:
            $ref: "#/components/schemas/Contact"
    MediaUpload:
      type: object
      required:
        - id
        - url
        - kind
        - mimeType
        - fileName
        - size
      properties:
        id:
          type: string
        url:
          type: string
          description: Workspace-scoped URL for later message requests.
        kind:
          type: string
          enum:
            - image
            - video
            - audio
            - document
        mimeType:
          type: string
        fileName:
          type: string
        size:
          type: integer
          description: File size in bytes.
    MessageEvent:
      type: object
      required:
        - offsetMs
        - label
      properties:
        offsetMs:
          type: integer
        label:
          type: string
    Message:
      type: object
      required:
        - id
        - requestId
        - sessionId
        - contactId
        - recipient
        - type
        - direction
        - status
        - body
        - latencyMs
        - createdAt
        - timeline
      properties:
        id:
          type: string
        requestId:
          type: string
        sessionId:
          type: string
        contactId:
          type:
            - string
            - "null"
        recipient:
          type: string
        contactName:
          type: string
          description: Name of the saved workspace contact whose normalized number matches
            the recipient; omitted when no contact matches.
        type:
          type: string
          enum:
            - text
            - image
            - video
            - audio
            - document
        direction:
          type: string
          enum:
            - outbound
            - inbound
        status:
          type: string
          enum:
            - queued
            - processing
            - sent
            - delivered
            - read
            - failed
            - received
        providerMessageId:
          type: string
        body:
          type: string
        mediaUrl:
          type: string
        latencyMs:
          type:
            - integer
            - "null"
        createdAt:
          type: integer
        timeline:
          type: array
          items:
            $ref: "#/components/schemas/MessageEvent"
        failureCode:
          type: string
          description: Set on failed messages. Stable reason code; new codes may be added,
            so treat unknown values as unknown.
          enum:
            - not_on_whatsapp
            - invalid_number
            - session_disconnected
            - rate_limited
            - timeout
            - media_upload_failed
            - media_unavailable
            - no_allowance
            - workspace_suspended
            - blocked_recipient
            - unknown
        failureReason:
          type: string
          description: Set on failed messages. A user-safe explanation of failureCode.
    MessagePage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Message"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    InboxConversation:
      type: object
      required:
        - id
        - sessionId
        - sessionName
        - contactId
        - contactName
        - recipient
        - assigneeId
        - assigneeName
        - status
        - lastMessageAt
        - lastMessage
        - awaitingSince
        - labels
      properties:
        id:
          type: string
        sessionId:
          type: string
        sessionName:
          type: string
        contactId:
          type:
            - string
            - "null"
        contactName:
          type: string
        recipient:
          type: string
        assigneeId:
          type:
            - string
            - "null"
        assigneeName:
          type: string
        status:
          type: string
          enum:
            - open
            - resolved
        lastMessageAt:
          type: integer
        lastMessage:
          $ref: "#/components/schemas/Message"
        awaitingSince:
          type:
            - integer
            - "null"
          description: Unix milliseconds of the customer's oldest message still waiting
            for a person's reply; null once the team replies or the conversation
            is resolved.
        labels:
          type: array
          items:
            type: string
    InboxNote:
      type: object
      required:
        - id
        - authorUserId
        - authorName
        - body
        - createdAt
      properties:
        id:
          type: string
        authorUserId:
          type: string
        authorName:
          type: string
        body:
          type: string
        createdAt:
          type: integer
    InboxQuickReply:
      type: object
      required:
        - id
        - shortcut
        - body
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
        shortcut:
          type: string
          description: Typed after a slash in the reply box, such as "hours" for /hours.
        body:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    InboxPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/InboxConversation"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    InboxAssignee:
      type: object
      required:
        - id
        - name
        - role
      properties:
        id:
          type: string
        name:
          type: string
        role:
          type: string
          enum:
            - owner
            - admin
            - developer
            - operator
            - billing
    InboxDetail:
      allOf:
        - $ref: "#/components/schemas/InboxConversation"
        - type: object
          required:
            - messages
            - notes
          properties:
            messages:
              type: array
              maxItems: 200
              items:
                $ref: "#/components/schemas/Message"
            notes:
              type: array
              description: Internal notes; customers never see them.
              items:
                $ref: "#/components/schemas/InboxNote"
    AutomationRule:
      type: object
      required:
        - id
        - name
        - trigger
        - eventName
        - conditionContains
        - replyText
        - workflowMode
        - enabled
        - createdAt
        - updatedAt
        - archivedAt
      properties:
        id:
          type: string
        name:
          type: string
        trigger:
          type: string
          enum:
            - message.received
            - custom_event
        eventName:
          type:
            - string
            - "null"
          description: Event name for a custom_event trigger; null for an inbound-message
            trigger.
        conditionContains:
          type: string
        replyText:
          type: string
        workflowMode:
          type: string
          enum:
            - simple
            - visual
        enabled:
          type: boolean
        createdAt:
          type: integer
        updatedAt:
          type: integer
        archivedAt:
          type: integer
          nullable: true
          description: Null until the rule is archived.
    WorkflowTrigger:
      oneOf:
        - $ref: "#/components/schemas/MessageReceivedTrigger"
        - $ref: "#/components/schemas/CustomEventTrigger"
      discriminator:
        propertyName: type
        mapping:
          message.received: "#/components/schemas/MessageReceivedTrigger"
          custom_event: "#/components/schemas/CustomEventTrigger"
    MessageReceivedTrigger:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          const: message.received
        reentry:
          type: string
          enum:
            - every_message
            - once
            - cooldown
          description: When the workflow may start again for the same contact. Omitted
            means every message. A workflow never starts for a contact who
            already has a run of it in progress, who is answering a menu or
            question, who was handed off to a person, or who started 30 runs in
            the last hour.
        cooldownSeconds:
          type: integer
          minimum: 60
          maximum: 31536000
          description: Required with the cooldown option; the time since this contact's
            last run of the workflow.
    CustomEventTrigger:
      type: object
      required:
        - type
        - eventName
      properties:
        type:
          type: string
          const: custom_event
        eventName:
          type: string
          pattern: ^[a-z][a-z0-9._-]{0,63}$
    WorkflowGraph:
      type: object
      required:
        - trigger
        - entryNodeId
        - nodes
      properties:
        trigger:
          $ref: "#/components/schemas/WorkflowTrigger"
        entryNodeId:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        nodes:
          type: array
          maxItems: 49
          description: The trigger plus these nodes may contain at most 50 total steps.
          items:
            $ref: "#/components/schemas/WorkflowNode"
        editorLayout:
          $ref: "#/components/schemas/WorkflowEditorLayout"
    WorkflowEditorLayout:
      type: object
      description: Optional editor-only node positions. The server ignores coordinates
        for workflow execution.
      properties:
        trigger:
          $ref: "#/components/schemas/WorkflowCanvasPosition"
        nodes:
          type: object
          description: Positions keyed by existing workflow node ID.
          additionalProperties:
            $ref: "#/components/schemas/WorkflowCanvasPosition"
    WorkflowCanvasPosition:
      type: object
      required:
        - x
        - y
      properties:
        x:
          type: number
          minimum: -100000
          maximum: 100000
        y:
          type: number
          minimum: -100000
          maximum: 100000
    WorkflowNode:
      oneOf:
        - $ref: "#/components/schemas/WorkflowConditionNode"
        - $ref: "#/components/schemas/WorkflowWaitNode"
        - $ref: "#/components/schemas/WorkflowSendTextNode"
        - $ref: "#/components/schemas/WorkflowSendMediaNode"
        - $ref: "#/components/schemas/WorkflowMenuNode"
        - $ref: "#/components/schemas/WorkflowAskNode"
        - $ref: "#/components/schemas/WorkflowAIReplyNode"
        - $ref: "#/components/schemas/WorkflowHandoffNode"
        - $ref: "#/components/schemas/WorkflowEndNode"
      discriminator:
        propertyName: type
        mapping:
          condition: "#/components/schemas/WorkflowConditionNode"
          wait: "#/components/schemas/WorkflowWaitNode"
          send_text: "#/components/schemas/WorkflowSendTextNode"
          send_media: "#/components/schemas/WorkflowSendMediaNode"
          menu: "#/components/schemas/WorkflowMenuNode"
          ask: "#/components/schemas/WorkflowAskNode"
          ai_reply: "#/components/schemas/WorkflowAIReplyNode"
          handoff: "#/components/schemas/WorkflowHandoffNode"
          end: "#/components/schemas/WorkflowEndNode"
    WorkflowConditionNode:
      type: object
      required:
        - id
        - type
        - field
        - operator
        - yes
        - no
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: condition
        field:
          type: string
          maxLength: 256
          pattern: ^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+$
          description: Dotted field path rooted at message or event.
        operator:
          type: string
          enum:
            - exists
            - equals
            - does_not_equal
            - contains
            - greater_than
            - less_than
        value:
          type:
            - string
            - number
            - boolean
            - "null"
          description: Omit for exists; otherwise a scalar comparison value.
        yes:
          type: string
          description: Node ID to execute when the condition matches.
        no:
          type: string
          description: Node ID to execute when the condition does not match.
    WorkflowWaitNode:
      type: object
      required:
        - id
        - type
        - waitSeconds
        - next
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: wait
        waitSeconds:
          type: integer
          minimum: 1
          maximum: 2592000
        next:
          type: string
          description: Node ID to execute after the wait.
    WorkflowSendTextNode:
      type: object
      required:
        - id
        - type
        - body
        - next
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: send_text
        sessionId:
          type: string
          maxLength: 64
          description: Optional connected workspace session; message-trigger flows use the
            triggering session. Required for customer-event flows.
        recipientField:
          type: string
          maxLength: 256
          pattern: ^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+$
          description: Recipient phone-number field in trigger data; required for
            customer-event flows.
        body:
          type: string
          minLength: 1
          maxLength: 4096
          description: Plain text; placeholders use paths such as {{message.text}}. The
            server enforces a 4,096-byte UTF-8 limit after interpolation.
        next:
          type: string
          description: Node ID to execute after the send.
    WorkflowSendMediaNode:
      type: object
      required:
        - id
        - type
        - mediaUrl
        - mediaType
        - next
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: send_media
        sessionId:
          type: string
          maxLength: 64
          description: Same rules as a text send.
        recipientField:
          type: string
          maxLength: 256
          description: Same rules as a text send.
        mediaUrl:
          type: string
          description: The `url` returned by `POST /v1/media`.
        mediaType:
          type: string
          enum:
            - image
            - video
            - audio
            - document
          description: The uploaded file's kind.
        body:
          type: string
          maxLength: 4096
          description: Optional caption with the same placeholders as a text send. Audio
            takes no caption.
        next:
          type: string
    WorkflowMenuOption:
      type: object
      required:
        - id
        - label
        - next
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        label:
          type: string
          minLength: 1
          maxLength: 100
        next:
          type: string
          description: Node ID to run when the contact picks this option.
    WorkflowMenuNode:
      type: object
      required:
        - id
        - type
        - body
        - options
        - fallback
        - timeoutSeconds
      description: Sends the message followed by numbered options, then waits for the
        contact to reply with a number or an option's label. Paths may loop back
        to a menu, since it waits for a reply each time.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: menu
        body:
          type: string
          minLength: 1
          maxLength: 4096
        options:
          type: array
          minItems: 2
          maxItems: 10
          items:
            $ref: "#/components/schemas/WorkflowMenuOption"
        retryBody:
          type: string
          maxLength: 1024
          description: Sent above the menu again after an unrecognized reply. Two
            unrecognized replies are allowed before the fallback path.
        timeoutSeconds:
          type: integer
          minimum: 60
          maximum: 2592000
        fallback:
          type: string
          description: Node ID to run when the contact doesn't reply in time or keeps
            sending unrecognized replies.
    WorkflowAskNode:
      type: object
      required:
        - id
        - type
        - body
        - next
        - fallback
        - timeoutSeconds
      description: Sends a question and waits for the answer. A saved answer is
        available to later steps as vars.<saveAs>.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: ask
        body:
          type: string
          minLength: 1
          maxLength: 4096
        saveAs:
          type: string
          pattern: ^[a-z][a-z0-9_]{0,31}$
        inputType:
          type: string
          enum:
            - text
            - number
            - email
            - phone
          description: Answer check; omitted means any text.
        retryBody:
          type: string
          maxLength: 1024
        timeoutSeconds:
          type: integer
          minimum: 60
          maximum: 2592000
        next:
          type: string
          description: Node ID to run with a valid answer.
        fallback:
          type: string
          description: Node ID to run on timeout or repeated invalid answers.
    WorkflowAIReplyNode:
      type: object
      required:
        - id
        - type
        - next
        - fallback
      description: Replies to the contact's latest message using the workspace
        knowledge base and recent conversation. Each reply uses one of the
        plan's monthly AI replies. When the knowledge base doesn't cover the
        question, the contact asks for a person, AI isn't configured on the
        server, or the allowance is used up, the run takes the fallback path
        instead of sending.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: ai_reply
        body:
          type: string
          maxLength: 2000
          description: Optional extra instructions such as tone or what to mention. They
            can't override the rule to answer only from the knowledge base.
        next:
          type: string
          description: Node ID to run after the reply is sent.
        fallback:
          type: string
          description: Node ID to run when the AI can't or shouldn't answer.
    KnowledgeEntryInput:
      type: object
      required:
        - kind
        - title
        - content
      properties:
        kind:
          type: string
          enum:
            - faq
            - note
          description: "faq: title is the question and content the answer. note: a titled
            block of text such as a price list or policy."
        title:
          type: string
          minLength: 1
          maxLength: 300
        content:
          type: string
          minLength: 1
          maxLength: 20000
    KnowledgeEntry:
      allOf:
        - $ref: "#/components/schemas/KnowledgeEntryInput"
        - type: object
          required:
            - id
            - createdAt
            - updatedAt
          properties:
            id:
              type: string
            createdAt:
              type: integer
              description: Unix time in milliseconds.
            updatedAt:
              type: integer
              description: Unix time in milliseconds.
    KnowledgeBase:
      type: object
      required:
        - items
        - usage
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/KnowledgeEntry"
        usage:
          type: object
          required:
            - entries
            - chars
            - maxChars
            - mode
            - indexedChunks
            - totalChunks
          properties:
            entries:
              type: integer
            chars:
              type: integer
            maxChars:
              "0": null
              type: integer
              description: 100
              000 characters: null
              or 2: null
              000 when the server has an embeddings provider.: null
            mode:
              type: string
              enum:
                - inline
                - search
              description: "inline: AI replies read the whole knowledge base. search: it is
                larger than 100,000 characters, so each reply reads the parts
                most relevant to the conversation."
            indexedChunks:
              type: integer
              description: Searchable pieces already embedded. Zero when the server has no
                embeddings provider.
            totalChunks:
              type: integer
    WorkflowHandoffNode:
      type: object
      required:
        - id
        - type
      description: Ends the run, reopens the contact's inbox conversation, and stops
        all workflows from starting for the contact for suppressSeconds.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: handoff
        suppressSeconds:
          type: integer
          minimum: -1
          maximum: 31536000
          description: -1 pauses workflows for this contact forever.
    WorkflowEndNode:
      type: object
      required:
        - id
        - type
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,64}$
        type:
          type: string
          const: end
        suppressSeconds:
          type: integer
          minimum: -1
          maximum: 31536000
          description: Optional. Stops this workflow from starting again for the contact
            for this long; -1 means forever.
    CreateAutomationFlowRequest:
      type: object
      required:
        - name
        - graph
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 128
        graph:
          $ref: "#/components/schemas/WorkflowGraph"
    AutomationDraft:
      type: object
      required:
        - automationId
        - revision
        - graph
      properties:
        automationId:
          type: string
        revision:
          type: integer
          minimum: 1
        graph:
          $ref: "#/components/schemas/WorkflowGraph"
    SaveAutomationDraftRequest:
      type: object
      required:
        - expectedRevision
        - graph
      properties:
        expectedRevision:
          type: integer
          minimum: 1
        graph:
          $ref: "#/components/schemas/WorkflowGraph"
    WorkflowValidationIssue:
      type: object
      required:
        - code
        - message
      properties:
        nodeId:
          type: string
        field:
          type: string
        code:
          type: string
        message:
          type: string
          description: Safe
          customer-facing validation text.: null
    WorkflowValidationResult:
      type: object
      required:
        - valid
        - issues
      properties:
        valid:
          type: boolean
        issues:
          type: array
          items:
            $ref: "#/components/schemas/WorkflowValidationIssue"
    WorkflowSimulationNodeResult:
      type: object
      required:
        - nodeId
        - type
        - outcome
      properties:
        nodeId:
          type: string
        type:
          type: string
          enum:
            - condition
            - wait
            - send_text
            - send_media
            - menu
            - ask
            - ai_reply
            - handoff
            - end
        outcome:
          type: string
          enum:
            - yes
            - no
            - would_wait
            - preview
            - would_ask
            - ai_reply
            - loops_back
            - handoff
            - end
            - failed
        resolvedText:
          type: string
        wakeAt:
          type: string
          format: date-time
    WorkflowSimulation:
      type: object
      required:
        - passed
        - nodeResults
        - branches
        - resolvedText
        - issues
      properties:
        passed:
          type: boolean
        nodeResults:
          type: array
          items:
            $ref: "#/components/schemas/WorkflowSimulationNodeResult"
        branches:
          type: object
          additionalProperties:
            type: string
            enum:
              - yes
              - no
        resolvedText:
          type: object
          additionalProperties:
            type: string
        issues:
          type: array
          items:
            $ref: "#/components/schemas/WorkflowValidationIssue"
    WorkflowTestResult:
      type: object
      required:
        - passed
        - draftRevision
        - graphHash
        - testedAt
        - simulation
      properties:
        passed:
          type: boolean
        draftRevision:
          type: integer
        graphHash:
          type: string
          pattern: ^[a-f0-9]{64}$
        testedAt:
          type: string
          format: date-time
        simulation:
          $ref: "#/components/schemas/WorkflowSimulation"
    TestAutomationDraftRequest:
      type: object
      required:
        - sample
      properties:
        sample:
          oneOf:
            - type: object
              required:
                - message
              properties:
                message:
                  type: object
                  additionalProperties: true
              additionalProperties: true
            - type: object
              required:
                - event
              properties:
                event:
                  type: object
                  additionalProperties: true
              additionalProperties: true
    PublishAutomationDraftRequest:
      type: object
      required:
        - expectedRevision
        - confirmSend
      properties:
        expectedRevision:
          type: integer
          minimum: 1
        confirmSend:
          type: boolean
          description: Must be true because published send steps can message real
            recipients.
    AutomationVersion:
      type: object
      required:
        - id
        - version
        - publishedAt
        - publishedBy
        - legacyCompat
      properties:
        id:
          type: string
        version:
          type: integer
          minimum: 1
        publishedAt:
          type: integer
          description: Unix time in milliseconds.
        publishedBy:
          type: string
        legacyCompat:
          type: boolean
    AutomationEventPayload:
      type: object
      description: Customer-defined event data. The encoded JSON object may contain at
        most 32 KiB.
      additionalProperties: true
    EventAcceptance:
      type: object
      required:
        - eventId
        - queuedWorkflows
        - skippedPaused
      properties:
        eventId:
          type: string
        queuedWorkflows:
          type: integer
          minimum: 0
        skippedPaused:
          type: integer
          minimum: 0
    AutomationRunStep:
      type: object
      required:
        - id
        - nodeId
        - type
        - status
        - attempts
        - wakeAt
        - startedAt
        - completedAt
      properties:
        id:
          type: string
        nodeId:
          type: string
        type:
          type: string
          enum:
            - condition
            - wait
            - send_text
            - send_media
            - menu
            - ask
            - ai_reply
            - end
            - legacy_reply
          description: One entry per visit
          so a menu the contact returns to appears more than once. legacy_reply is a synthetic step retained for migrated simple-automation run history.: null
        status:
          type: string
          enum:
            - pending
            - running
            - waiting
            - succeeded
            - failed
            - skipped
            - cancelled
        attempts:
          type: integer
          minimum: 0
        wakeAt:
          type:
            - integer
            - "null"
          description: Unix time in milliseconds.
        errorCode:
          type: string
        error:
          type: string
          description: Safe customer-facing error text.
        messageId:
          type: string
        startedAt:
          type:
            - integer
            - "null"
          description: Unix time in milliseconds.
        completedAt:
          type:
            - integer
            - "null"
          description: Unix time in milliseconds.
    AutomationRunDetail:
      type: object
      required:
        - id
        - automationId
        - sourceType
        - versionId
        - ruleName
        - status
        - attempts
        - availableAt
        - createdAt
        - updatedAt
        - completedAt
        - steps
      properties:
        id:
          type: string
        automationId:
          type: string
        sourceType:
          type: string
          enum:
            - message.received
            - custom_event
        triggerMessageId:
          type: string
        versionId:
          type: string
        ruleName:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - waiting
            - paused
            - succeeded
            - failed
            - cancelled
        attempts:
          type: integer
          minimum: 0
          description: Greatest retry count of any step; worker claim generations are not
            exposed.
        sessionId:
          type: string
        recipient:
          type: string
        replyMessageId:
          type: string
        lastError:
          type: string
          description: Safe summary; raw provider error text is not returned.
        availableAt:
          type: integer
          description: Unix time in milliseconds.
        createdAt:
          type: integer
          description: Unix time in milliseconds.
        updatedAt:
          type: integer
          description: Unix time in milliseconds.
        completedAt:
          type:
            - integer
            - "null"
          description: Unix time in milliseconds.
        steps:
          type: array
          items:
            $ref: "#/components/schemas/AutomationRunStep"
    AutomationRulePage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/AutomationRule"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    AutomationRun:
      type: object
      required:
        - id
        - automationId
        - triggerMessageId
        - sessionId
        - recipient
        - ruleName
        - status
        - attempts
        - availableAt
        - replyMessageId
        - lastError
        - completedAt
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
        automationId:
          type: string
        triggerMessageId:
          type: string
        sessionId:
          type: string
        recipient:
          type: string
        ruleName:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - waiting
            - paused
            - succeeded
            - failed
            - cancelled
        attempts:
          type: integer
        availableAt:
          type: integer
        replyMessageId:
          type:
            - string
            - "null"
        lastError:
          type: string
        completedAt:
          type:
            - integer
            - "null"
        createdAt:
          type: integer
        updatedAt:
          type: integer
    AutomationRunPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/AutomationRun"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    MessageDraftInput:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        type:
          type: string
          enum:
            - text
            - image
            - video
            - audio
            - document
        body:
          type: string
          maxLength: 4096
        mediaUrl:
          type: string
          description: Workspace media URL required for media drafts.
    MessageDraft:
      type: object
      required:
        - id
        - name
        - type
        - body
        - mediaUrl
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
          enum:
            - text
            - image
            - video
            - audio
            - document
        body:
          type: string
        mediaUrl:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CampaignFailureReason:
      type: object
      required:
        - reason
        - count
      properties:
        reason:
          type: string
        count:
          type: integer
    Campaign:
      type: object
      required:
        - id
        - name
        - audience
        - queued
        - processing
        - sent
        - delivered
        - read
        - failed
        - skipped
        - status
        - scheduledFor
        - scheduledAt
        - sessionId
        - message
        - messageType
        - mediaUrl
        - speed
        - createdAt
        - failedReasons
        - holdReason
        - deliveryRate
        - readRate
      properties:
        id:
          type: string
        name:
          type: string
        audience:
          type: integer
        queued:
          type: integer
        processing:
          type: integer
        sent:
          type: integer
        delivered:
          type: integer
        read:
          type: integer
        failed:
          type: integer
        skipped:
          type: integer
        status:
          type: string
          enum:
            - draft
            - scheduled
            - running
            - paused
            - completed
            - cancelled
            - failed
        scheduledFor:
          type: string
        scheduledAt:
          type:
            - integer
            - "null"
        sessionId:
          type: string
        message:
          type: string
        messageType:
          type: string
          enum:
            - text
            - image
            - video
            - audio
            - document
        mediaUrl:
          type: string
        speed:
          type: integer
        createdAt:
          type: integer
        failedReasons:
          type: array
          items:
            $ref: "#/components/schemas/CampaignFailureReason"
        holdReason:
          type: string
          description: Why a running broadcast is waiting (a new number's daily warm-up
            limit) or was paused automatically (a spike of failed messages).
            Empty otherwise.
        deliveryRate:
          type:
            - number
            - "null"
          description: Delivered or read recipients divided by recipients attempted (sent
            or failed), as a 0-1 fraction. Null when none were attempted.
        readRate:
          type:
            - number
            - "null"
          description: Read recipients divided by recipients attempted
          same denominator as deliveryRate. Null when none were attempted.: null
    CampaignPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Campaign"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    CampaignRecipient:
      type: object
      required:
        - id
        - contactId
        - number
        - status
        - messageId
        - attempts
        - failureReason
        - queuedAt
        - sentAt
        - deliveredAt
        - readAt
      properties:
        id:
          type: string
        contactId:
          type:
            - string
            - "null"
        number:
          type: string
        contactName:
          type: string
          description: Name of the saved workspace contact whose normalized number
            matches; omitted when no contact matches.
        status:
          type: string
        messageId:
          type:
            - string
            - "null"
        attempts:
          type: integer
        failureReason:
          type: string
        queuedAt:
          type: integer
        sentAt:
          type:
            - integer
            - "null"
        deliveredAt:
          type:
            - integer
            - "null"
        readAt:
          type:
            - integer
            - "null"
    CampaignProgress:
      type: object
      required:
        - campaignId
        - total
        - queued
        - processing
        - sent
        - delivered
        - read
        - failed
        - skipped
        - failedReasons
      properties:
        campaignId:
          type: string
        total:
          type: integer
        queued:
          type: integer
        processing:
          type: integer
        sent:
          type: integer
        delivered:
          type: integer
        read:
          type: integer
        failed:
          type: integer
        skipped:
          type: integer
        failedReasons:
          type: array
          items:
            $ref: "#/components/schemas/CampaignFailureReason"
    CampaignDetail:
      type: object
      required:
        - campaign
        - recipients
        - page
        - pageSize
        - total
      properties:
        campaign:
          $ref: "#/components/schemas/Campaign"
        recipients:
          type: array
          items:
            $ref: "#/components/schemas/CampaignRecipient"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    WebhookEndpoint:
      type: object
      required:
        - id
        - name
        - url
        - events
        - status
        - delivered
        - failed
        - createdAt
      properties:
        id:
          type: string
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - active
            - paused
            - failing
        delivered:
          type: integer
        failed:
          type: integer
        createdAt:
          type: integer
          description: Creation time in Unix milliseconds.
    WebhookEndpointSummary:
      type: object
      required:
        - delivered
        - failed
        - failing
      properties:
        delivered:
          type: integer
        failed:
          type: integer
        failing:
          type: integer
    WebhookPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
        - summary
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEndpoint"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
        summary:
          $ref: "#/components/schemas/WebhookEndpointSummary"
    WebhookSecretResult:
      type: object
      required:
        - endpoint
        - secret
      properties:
        endpoint:
          $ref: "#/components/schemas/WebhookEndpoint"
        secret:
          type: string
          readOnly: true
          description: Signing secret shown only when created or rotated.
    WebhookTestResult:
      type: object
      required:
        - deliveryId
        - status
      properties:
        deliveryId:
          type: string
        status:
          type: string
    WebhookAttempt:
      type: object
      required:
        - id
        - endpointId
        - event
        - status
        - durationMs
        - attempt
        - createdAt
        - requestHeaders
        - requestBody
        - responseBody
        - timing
      properties:
        id:
          type: string
        endpointId:
          type: string
        event:
          type: string
        status:
          type: integer
          description: HTTP status returned by the endpoint; zero when no response was
            received.
        durationMs:
          type: integer
        attempt:
          type: integer
        createdAt:
          type: integer
          description: Attempt time in Unix milliseconds.
        requestHeaders:
          type: object
          additionalProperties:
            type: string
        requestBody:
          type: string
        responseBody:
          type: string
        timing:
          type: object
          additionalProperties:
            type: integer
    WebhookAttemptPage:
      type: object
      required:
        - items
        - page
        - pageSize
        - total
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/WebhookAttempt"
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
    APIError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Stable machine-readable error code. Message endpoints may return
            free_caption_required, free_audio_unavailable,
            message_body_too_long, or quota_exceeded; Free checkout returns
            free_checkout_unavailable. API keys missing a scope get
            insufficient_scope; user-only operations answer API keys with
            interactive_session_required. The staff console (/v1/support-admin)
            answers support_staff_only (403, not an active verified staff
            member), staff_session_expired (401, sign in again),
            staff_mfa_required (403), staff_permission_denied (403, with
            requiredPermission), reason_required (422), and for staff management
            staff_account_not_found, staff_exists, staff_not_found,
            staff_self_edit and staff_last_admin.
        message:
          type: string
        requiredScope:
          type: string
          description: The API-key scope the request lacked. Present with
            insufficient_scope.
        requiredPermission:
          type: string
          description: The staff permission the request lacked. Present with
            staff_permission_denied.
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          $ref: "#/components/schemas/APIError"
