> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enginy.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Validate campaign

> Validate a simplified public campaign payload against the public step schema without creating a campaign. Use this before `POST /v1/campaigns` when you already have a proposed request body. This validates only the submitted payload and does not read or inspect an existing saved campaign.

This endpoint validates only the submitted request body. It does not load, inspect, or verify an already-saved campaign in Enginy.

Create a draft campaign from the public `steps` format.

How to structure the request:
- Start `steps` with the first action that should happen.
- Keep adding the next action in the order it should run.
- Use `condition` when you want to branch on lead data.
- Use `linkedin_connection` with `waitForAcceptance` when you want to branch on connection acceptance after sending a connection request. Omit `waitForAcceptance` for a fire-and-forget connection request.
- Use `linkedin_message` with `content`, `attachment`, or both. Attachment-only messages are valid.
- Use `whatsapp_message` only when the workspace and sending identity are configured for WhatsApp.
- Put shared follow-up work after the branching step instead of duplicating the same steps inside both branches.

Before you send a request:
- Send an ordered `steps` array that describes the campaign flow from first action to last action.
- Use branch arrays on `condition` and `linkedin_connection` steps to describe branch-specific work.
- Place shared follow-up steps after a branching step when both live branches should continue together.
- `waitForAcceptance.unit` must be `days` and `waitForAcceptance.value` must be an integer greater than or equal to `1`.
- Use `GET /v1/tasks/owners` first when your campaign includes `task` steps. If owners are returned, every `task` step must include `ownerId`.
- Validation errors use full paths like `steps[0].onTrue[1].subject` so nested input issues are easy to fix.
- Personalization placeholders use Enginy single-brace syntax such as `{firstName}`, `{company}`, and `{identity.name}`. Do not send Handlebars-style placeholders like `{{firstName}}`.

Supported `condition.type` values:
- `has_linkedin_profile`: `onTrue` runs when the lead has a LinkedIn profile.
- `has_professional_email`: `onTrue` runs when the lead has a professional email.
- `is_already_connected`: `onTrue` runs when the lead is already a LinkedIn connection.
- `has_been_contacted`: `onTrue` runs when the lead has already been contacted.
- `has_whatsapp_account`: `onTrue` runs when the lead has a WhatsApp account.
- `email_opened`: `onTrue` runs when the lead opens the preceding email within `waitFor`. Must follow an `email` step. Requires `waitFor` (`value`, `unit`).
- `email_clicked`: `onTrue` runs when the lead clicks a link in the preceding email within `waitFor`. Must follow an `email` step. Requires `waitFor` (`value`, `unit`).
- `task_completed`: `onTrue` runs when the preceding `task` step is marked complete within `waitFor`. Must follow a `task` step. Requires `waitFor` (`value`, `unit`).
- `connection_accepted`: `onTrue` runs when the lead accepts a previous LinkedIn connection request within `waitFor`. Requires `waitFor` (`value`, `unit`).
- `lead_field`: `onTrue` runs when the contact `field` matches the `operator` (and `value`, where applicable). Use this to branch on any built-in column or custom field. Requires `field` and `operator`. `value` is required for every operator except `IS_EMPTY` and `IS_NOT_EMPTY`. Supported operators: `EQUALS`, `NOT_EQUALS`, `CONTAINS`, `NOT_CONTAINS`, `GREATER_THAN`, `LESS_THAN`, `GREATER_THAN_OR_EQUALS`, `LESS_THAN_OR_EQUALS`, `IS_EMPTY`, `IS_NOT_EMPTY`.

Branch fields:
- `condition` uses `onTrue` and `onFalse`.
- `linkedin_connection` uses `onAccepted` and `onNotAccepted` only when `waitForAcceptance` is set.
- Use `[{ "type": "end" }]` when a branch should stop explicitly.

Routing into another campaign:
- Use `add_to_another_campaign` with a `campaignId` to route the lead into an existing campaign in the same workspace.
- The referenced `campaignId` must belong to the same client as the API key. If you are cloning campaigns across workspaces, remap each `campaignId` to the equivalent campaign in the destination workspace before posting.
- Can be followed by additional steps in the same branch.

For examples and a full step-by-step guide, see `/api-reference/campaigns-guide` in the docs site.

    > **Required scope:** `CAMPAIGNS_WRITE`
    >
    > **Rate limit:** 30 requests per minute



## OpenAPI

````yaml https://openapi.enginy.ai/definitions.json post /v1/campaigns/validate
openapi: 3.1.0
info:
  title: Enginy API
  version: 1.0.0
  description: Public API for Enginy platform
servers:
  - url: https://openapi.enginy.ai
    description: Configured server
security: []
tags:
  - name: AI Variables
    description: >-
      Manage AI variables and discover the entity fields you can reference in
      prompts and entity requests.
  - name: Inbox
    description: >-
      Inbox endpoints for listing contact threads, reading thread messages,
      managing tags, and sending manual replies.
  - name: Workflows
    description: >-
      Build, validate, publish, run, and inspect advanced (graph) workflows,
      plus the block/condition catalog needed to author a workflow plan.
paths:
  /v1/campaigns/validate:
    post:
      tags:
        - Campaigns
      summary: Validate campaign
      description: >-
        Validate a simplified public campaign payload against the public step
        schema without creating a campaign. Use this before `POST /v1/campaigns`
        when you already have a proposed request body. This validates only the
        submitted payload and does not read or inspect an existing saved
        campaign.


        This endpoint validates only the submitted request body. It does not
        load, inspect, or verify an already-saved campaign in Enginy.


        Create a draft campaign from the public `steps` format.


        How to structure the request:

        - Start `steps` with the first action that should happen.

        - Keep adding the next action in the order it should run.

        - Use `condition` when you want to branch on lead data.

        - Use `linkedin_connection` with `waitForAcceptance` when you want to
        branch on connection acceptance after sending a connection request. Omit
        `waitForAcceptance` for a fire-and-forget connection request.

        - Use `linkedin_message` with `content`, `attachment`, or both.
        Attachment-only messages are valid.

        - Use `whatsapp_message` only when the workspace and sending identity
        are configured for WhatsApp.

        - Put shared follow-up work after the branching step instead of
        duplicating the same steps inside both branches.


        Before you send a request:

        - Send an ordered `steps` array that describes the campaign flow from
        first action to last action.

        - Use branch arrays on `condition` and `linkedin_connection` steps to
        describe branch-specific work.

        - Place shared follow-up steps after a branching step when both live
        branches should continue together.

        - `waitForAcceptance.unit` must be `days` and `waitForAcceptance.value`
        must be an integer greater than or equal to `1`.

        - Use `GET /v1/tasks/owners` first when your campaign includes `task`
        steps. If owners are returned, every `task` step must include `ownerId`.

        - Validation errors use full paths like `steps[0].onTrue[1].subject` so
        nested input issues are easy to fix.

        - Personalization placeholders use Enginy single-brace syntax such as
        `{firstName}`, `{company}`, and `{identity.name}`. Do not send
        Handlebars-style placeholders like `{{firstName}}`.


        Supported `condition.type` values:

        - `has_linkedin_profile`: `onTrue` runs when the lead has a LinkedIn
        profile.

        - `has_professional_email`: `onTrue` runs when the lead has a
        professional email.

        - `is_already_connected`: `onTrue` runs when the lead is already a
        LinkedIn connection.

        - `has_been_contacted`: `onTrue` runs when the lead has already been
        contacted.

        - `has_whatsapp_account`: `onTrue` runs when the lead has a WhatsApp
        account.

        - `email_opened`: `onTrue` runs when the lead opens the preceding email
        within `waitFor`. Must follow an `email` step. Requires `waitFor`
        (`value`, `unit`).

        - `email_clicked`: `onTrue` runs when the lead clicks a link in the
        preceding email within `waitFor`. Must follow an `email` step. Requires
        `waitFor` (`value`, `unit`).

        - `task_completed`: `onTrue` runs when the preceding `task` step is
        marked complete within `waitFor`. Must follow a `task` step. Requires
        `waitFor` (`value`, `unit`).

        - `connection_accepted`: `onTrue` runs when the lead accepts a previous
        LinkedIn connection request within `waitFor`. Requires `waitFor`
        (`value`, `unit`).

        - `lead_field`: `onTrue` runs when the contact `field` matches the
        `operator` (and `value`, where applicable). Use this to branch on any
        built-in column or custom field. Requires `field` and `operator`.
        `value` is required for every operator except `IS_EMPTY` and
        `IS_NOT_EMPTY`. Supported operators: `EQUALS`, `NOT_EQUALS`, `CONTAINS`,
        `NOT_CONTAINS`, `GREATER_THAN`, `LESS_THAN`, `GREATER_THAN_OR_EQUALS`,
        `LESS_THAN_OR_EQUALS`, `IS_EMPTY`, `IS_NOT_EMPTY`.


        Branch fields:

        - `condition` uses `onTrue` and `onFalse`.

        - `linkedin_connection` uses `onAccepted` and `onNotAccepted` only when
        `waitForAcceptance` is set.

        - Use `[{ "type": "end" }]` when a branch should stop explicitly.


        Routing into another campaign:

        - Use `add_to_another_campaign` with a `campaignId` to route the lead
        into an existing campaign in the same workspace.

        - The referenced `campaignId` must belong to the same client as the API
        key. If you are cloning campaigns across workspaces, remap each
        `campaignId` to the equivalent campaign in the destination workspace
        before posting.

        - Can be followed by additional steps in the same branch.


        For examples and a full step-by-step guide, see
        `/api-reference/campaigns-guide` in the docs site.

            > **Required scope:** `CAMPAIGNS_WRITE`
            >
            > **Rate limit:** 30 requests per minute
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  description: Campaign name.
                description:
                  type:
                    - string
                    - 'null'
                  description: Optional campaign description.
                identityId:
                  type:
                    - integer
                    - 'null'
                  exclusiveMinimum: 0
                shouldAutomaticallySend:
                  type: boolean
                shouldSendTrackingPixel:
                  type: boolean
                shouldTrackEmailLinks:
                  type: boolean
                excludeContactedLeads:
                  type: boolean
                allowedEmails:
                  type: array
                  items:
                    type: string
                  description: Allowed sender email addresses for this campaign.
                steps:
                  type: array
                  items:
                    oneOf:
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - email
                          subject:
                            type: string
                            minLength: 1
                          content:
                            type: string
                            minLength: 1
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - subject
                          - content
                        additionalProperties: false
                        title: email
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_message
                          content:
                            type: string
                            minLength: 1
                            description: >-
                              LinkedIn message body. Required unless
                              `attachment` is provided.
                          attachment:
                            type: string
                            minLength: 1
                            description: >-
                              Optional URL or storage key of the file to send
                              alongside the message.
                          attachmentName:
                            type: string
                            minLength: 1
                            description: Optional display name for the attachment.
                          contentType:
                            type: string
                            enum:
                              - AUDIO
                              - IMAGE
                              - FILE
                              - VIDEO
                            description: >-
                              Attachment kind. Recommended when `attachment` is
                              set. Use `VIDEO` for video files.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                        additionalProperties: false
                        title: linkedin_message
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_message_bundle
                          messages:
                            type: array
                            items:
                              type: object
                              properties:
                                content:
                                  type: string
                                  minLength: 1
                                  description: Text body of the bundle message.
                                attachment:
                                  type: string
                                  minLength: 1
                                  description: URL or storage key of the file to send.
                                attachmentName:
                                  type: string
                                  minLength: 1
                                  description: Display name for the attachment.
                                contentType:
                                  type: string
                                  enum:
                                    - AUDIO
                                    - IMAGE
                                    - FILE
                                    - VIDEO
                                  description: >-
                                    Attachment kind. Recommended when
                                    `attachment` is set. Use `VIDEO` for video
                                    files.
                              additionalProperties: false
                            minItems: 1
                            description: >-
                              Ordered list of messages sent back-to-back as a
                              single bundle.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - messages
                        additionalProperties: false
                        title: linkedin_message_bundle
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_voice_message
                          content:
                            type: string
                            minLength: 1
                            description: >-
                              Text body that will be synthesized to speech via
                              the configured `voiceId`.
                          voiceId:
                            type: string
                            minLength: 1
                            description: >-
                              ID of a system voice or an identity-cloned voice.
                              Use `GET /v1/voices` to list valid voice IDs.
                          attachment:
                            type: string
                            minLength: 1
                            description: >-
                              URL or storage key for a pre-recorded audio
                              message.
                          attachmentName:
                            type: string
                            minLength: 1
                            description: Optional display name for the audio file.
                          contentType:
                            type: string
                            enum:
                              - AUDIO
                            description: Set to `AUDIO` when `attachment` is provided.
                          voiceSettings:
                            type: object
                            properties:
                              voiceSpeed:
                                type: number
                                minimum: 0.5
                                maximum: 2
                                description: >-
                                  Speech rate multiplier. Range 0.5–2.0.
                                  Defaults are applied server-side when omitted.
                              voiceStability:
                                type: number
                                minimum: 0
                                maximum: 100
                                description: >-
                                  Voice stability percentage (0–100). Higher is
                                  steadier, lower is more expressive.
                              backgroundNoise:
                                type: string
                                minLength: 1
                                description: >-
                                  Optional background noise preset (e.g. `none`,
                                  `office`). Server defaults to `none`.
                              backgroundVolume:
                                type: number
                                minimum: 0
                                maximum: 100
                                description: >-
                                  Background noise volume (0–100). Ignored when
                                  `backgroundNoise` is `none`.
                            additionalProperties: false
                            description: >-
                              Optional fine-tuning of the synthesized voice and
                              background audio.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                        additionalProperties: false
                        title: linkedin_voice_message
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_inmail
                          subject:
                            type: string
                            minLength: 1
                          content:
                            type: string
                            minLength: 1
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - subject
                          - content
                        additionalProperties: false
                        title: linkedin_inmail
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_visit_profile
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                        additionalProperties: false
                        title: linkedin_visit_profile
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_like_last_post
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                          reactionType:
                            type: string
                            minLength: 1
                        required:
                          - type
                        additionalProperties: false
                        title: linkedin_like_last_post
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - whatsapp_message
                          content:
                            type: string
                            minLength: 1
                            description: >-
                              WhatsApp message body. Requires WhatsApp support
                              on the workspace and identity.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - content
                        additionalProperties: false
                        title: whatsapp_message
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - condition
                          condition:
                            oneOf:
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - has_linkedin_profile
                                    description: Lead has a LinkedIn profile.
                                  field:
                                    type: string
                                    enum:
                                      - linkedinProfileUrl
                                      - linkedInProfileId
                                    description: >-
                                      Stored lead field to check. Defaults to
                                      `linkedinProfileUrl`.
                                required:
                                  - type
                                additionalProperties: false
                                title: has_linkedin_profile
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - has_professional_email
                                    description: Lead has a professional email.
                                  verificationStatuses:
                                    type: array
                                    items:
                                      type: string
                                    description: >-
                                      Optional email verification statuses to
                                      include when matching.
                                  includeEmpty:
                                    type: boolean
                                    description: >-
                                      Whether empty professional email values
                                      match.
                                required:
                                  - type
                                additionalProperties: false
                                title: has_professional_email
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - is_already_connected
                                    description: Lead is already a LinkedIn connection.
                                required:
                                  - type
                                additionalProperties: false
                                title: is_already_connected
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - has_been_contacted
                                    description: Lead has already been contacted.
                                required:
                                  - type
                                additionalProperties: false
                                title: has_been_contacted
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - has_whatsapp_account
                                    description: Lead has a WhatsApp account.
                                required:
                                  - type
                                additionalProperties: false
                                title: has_whatsapp_account
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - email_opened
                                    description: >-
                                      Branch on whether the preceding email is
                                      opened within `waitFor`.
                                  waitFor:
                                    type: object
                                    properties:
                                      value:
                                        type: number
                                        exclusiveMinimum: 0
                                        description: Wait amount.
                                      unit:
                                        type: string
                                        enum:
                                          - seconds
                                          - minutes
                                          - hours
                                          - days
                                        description: Wait unit.
                                    required:
                                      - value
                                      - unit
                                    additionalProperties: false
                                    description: >-
                                      How long to wait for the lead to open the
                                      preceding email. Required.
                                required:
                                  - type
                                  - waitFor
                                additionalProperties: false
                                title: email_opened
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - email_clicked
                                    description: >-
                                      Branch on whether a link in the preceding
                                      email is clicked within `waitFor`.
                                  waitFor:
                                    type: object
                                    properties:
                                      value:
                                        type: number
                                        exclusiveMinimum: 0
                                        description: Wait amount.
                                      unit:
                                        type: string
                                        enum:
                                          - seconds
                                          - minutes
                                          - hours
                                          - days
                                        description: Wait unit.
                                    required:
                                      - value
                                      - unit
                                    additionalProperties: false
                                    description: >-
                                      How long to wait for the lead to click a
                                      link in the preceding email. Required.
                                required:
                                  - type
                                  - waitFor
                                additionalProperties: false
                                title: email_clicked
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - task_completed
                                    description: >-
                                      Branch on whether the preceding task is
                                      marked complete within `waitFor`.
                                  waitFor:
                                    type: object
                                    properties:
                                      value:
                                        type: number
                                        exclusiveMinimum: 0
                                        description: Wait amount.
                                      unit:
                                        type: string
                                        enum:
                                          - seconds
                                          - minutes
                                          - hours
                                          - days
                                        description: Wait unit.
                                    required:
                                      - value
                                      - unit
                                    additionalProperties: false
                                    description: >-
                                      How long to wait for the task to be marked
                                      complete. Required.
                                required:
                                  - type
                                  - waitFor
                                additionalProperties: false
                                title: task_completed
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - connection_accepted
                                    description: >-
                                      Branch on whether the lead accepts a
                                      previous LinkedIn connection request.
                                  waitFor:
                                    type: object
                                    properties:
                                      value:
                                        type: number
                                        exclusiveMinimum: 0
                                        description: Wait amount.
                                      unit:
                                        type: string
                                        enum:
                                          - seconds
                                          - minutes
                                          - hours
                                          - days
                                        description: Wait unit.
                                    required:
                                      - value
                                      - unit
                                    additionalProperties: false
                                    description: >-
                                      How long to wait for the lead to accept
                                      the previous LinkedIn connection request.
                                      Required.
                                required:
                                  - type
                                  - waitFor
                                additionalProperties: false
                                title: connection_accepted
                              - type: object
                                properties:
                                  type:
                                    type: string
                                    enum:
                                      - lead_field
                                    description: Branch on a lead field value.
                                  field:
                                    type: string
                                    minLength: 1
                                    description: >-
                                      Built-in column name (e.g.
                                      `professionalEmail`, `mobilePhone`) or
                                      custom field name.
                                  operator:
                                    type: string
                                    enum:
                                      - EQUALS
                                      - NOT_EQUALS
                                      - CONTAINS
                                      - NOT_CONTAINS
                                      - GREATER_THAN
                                      - LESS_THAN
                                      - GREATER_THAN_OR_EQUALS
                                      - LESS_THAN_OR_EQUALS
                                      - IS_EMPTY
                                      - IS_NOT_EMPTY
                                    description: >-
                                      Comparison operator. `IS_EMPTY` and
                                      `IS_NOT_EMPTY` ignore `value`; all others
                                      require it.
                                  value:
                                    anyOf:
                                      - type: string
                                      - type: number
                                      - type: boolean
                                      - type: array
                                        items:
                                          type: string
                                      - type: array
                                        items:
                                          type: number
                                      - type: array
                                        items:
                                          type: boolean
                                    description: >-
                                      Comparison value. Required for every
                                      operator except `IS_EMPTY` and
                                      `IS_NOT_EMPTY`.
                                required:
                                  - type
                                  - field
                                  - operator
                                additionalProperties: false
                                title: lead_field
                            description: >-
                              Condition evaluated at this point in the sequence.
                              Supported `condition.type` values are
                              `has_linkedin_profile`, `has_professional_email`,
                              `is_already_connected`, `has_been_contacted`,
                              `has_whatsapp_account`, `email_opened`,
                              `email_clicked`, `task_completed`,
                              `connection_accepted`, and `lead_field`. The
                              email, task, and connection-accepted variants
                              require a `waitFor` object with `value` and
                              `unit`. The `lead_field` variant requires `field`
                              and `operator`, and a `value` for every operator
                              except `IS_EMPTY` and `IS_NOT_EMPTY`.
                          onTrue:
                            type: array
                            items:
                              type: object
                              properties:
                                type:
                                  type: string
                                  description: Nested branch step type.
                              required:
                                - type
                              additionalProperties: {}
                              description: >-
                                Nested branch step. Use the same step payload
                                shape as top-level `steps` items.
                            minItems: 1
                            description: >-
                              Steps to run when the condition matches. If
                              provided, it must contain at least one step. Use
                              `{ "type": "end" }` to stop that branch
                              explicitly.
                          onFalse:
                            type: array
                            items:
                              type: object
                              properties:
                                type:
                                  type: string
                                  description: Nested branch step type.
                              required:
                                - type
                              additionalProperties: {}
                              description: >-
                                Nested branch step. Use the same step payload
                                shape as top-level `steps` items.
                            minItems: 1
                            description: >-
                              Steps to run when the condition does not match. If
                              provided, it must contain at least one step. Use
                              `{ "type": "end" }` to stop that branch
                              explicitly.
                        required:
                          - type
                          - condition
                        additionalProperties: false
                        title: condition
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - linkedin_connection
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                          content:
                            type: string
                            minLength: 1
                          waitForAcceptance:
                            type: object
                            properties:
                              value:
                                type: integer
                                minimum: 1
                                description: whole number of days to wait. Minimum 1 day.
                              unit:
                                type: string
                                enum:
                                  - days
                                description: Connection wait unit. Must be `days`.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                            description: >-
                              Optional. Provide an acceptance window to branch
                              the flow on whether the lead accepted the request.
                              Omit to send the request without an inline
                              acceptance check; the campaign continues with
                              whatever follows.
                          onAccepted:
                            type: array
                            items:
                              type: object
                              properties:
                                type:
                                  type: string
                                  description: Nested branch step type.
                              required:
                                - type
                              additionalProperties: {}
                              description: >-
                                Nested branch step. Use the same step payload
                                shape as top-level `steps` items.
                            minItems: 1
                            description: >-
                              Accepted branch steps. Only valid when
                              `waitForAcceptance` is set. If provided, it must
                              contain at least one step. Use `{ "type": "end" }`
                              to stop that branch explicitly.
                          onNotAccepted:
                            type: array
                            items:
                              type: object
                              properties:
                                type:
                                  type: string
                                  description: Nested branch step type.
                              required:
                                - type
                              additionalProperties: {}
                              description: >-
                                Nested branch step. Use the same step payload
                                shape as top-level `steps` items.
                            minItems: 1
                            description: >-
                              Not-accepted branch steps. Only valid when
                              `waitForAcceptance` is set. If provided, it must
                              contain at least one step. Use `{ "type": "end" }`
                              to stop that branch explicitly.
                        required:
                          - type
                        additionalProperties: false
                        title: linkedin_connection
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - task
                          taskType:
                            type: string
                            minLength: 1
                            description: >-
                              Task type. Allowed values depend on the connected
                              CRM. For HubSpot use one of: CALL, EMAIL, or TODO.
                              Do not invent variants such as "COLD_CALL" — use
                              the closest standard value (e.g. "CALL").
                            example: CALL
                          title:
                            type: string
                            minLength: 1
                            description: Required task title shown in the UI.
                          note:
                            type: string
                            minLength: 1
                            description: Optional task note shown in the UI.
                          ownerId:
                            type:
                              - string
                              - 'null'
                            minLength: 1
                            description: >-
                              Required when `GET /v1/tasks/owners` returns one
                              or more owners.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - taskType
                          - title
                        additionalProperties: false
                        title: task
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - add_to_another_campaign
                            description: >-
                              Routes the lead into another campaign owned by the
                              same workspace, then ends this branch.
                          campaignId:
                            type: integer
                            exclusiveMinimum: 0
                            description: >-
                              ID of a campaign owned by the same client as the
                              API key making the request.
                          delay:
                            type: object
                            properties:
                              value:
                                type: number
                                minimum: 0
                                description: Delay amount.
                              unit:
                                type: string
                                enum:
                                  - seconds
                                  - minutes
                                  - hours
                                  - days
                                description: Delay unit.
                            required:
                              - value
                              - unit
                            additionalProperties: false
                        required:
                          - type
                          - campaignId
                        additionalProperties: false
                        title: add_to_another_campaign
                      - type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - end
                        required:
                          - type
                        additionalProperties: false
                        title: end
                  minItems: 1
                  description: >-
                    Ordered campaign steps. Use branch arrays like `onTrue`,
                    `onFalse`, `onAccepted`, and `onNotAccepted` for branching.
              required:
                - name
                - steps
              additionalProperties: false
            examples:
              conditionFirst:
                summary: Start with a condition and run different work on each branch
                value:
                  name: Email or fallback task
                  identityId: 323
                  steps:
                    - type: condition
                      condition:
                        type: has_professional_email
                      onTrue:
                        - type: email
                          subject: Great to connect
                          content: Sharing a quick overview.
                      onFalse:
                        - type: task
                          taskType: TODO
                          title: Find email first
                          ownerId: owner-1
              linkedinAcceptance:
                summary: >-
                  Branch on LinkedIn connection acceptance and continue after
                  the branch
                value:
                  name: LinkedIn acceptance flow
                  identityId: 323
                  steps:
                    - type: linkedin_connection
                      delay:
                        value: 1
                        unit: days
                      waitForAcceptance:
                        value: 5
                        unit: days
                      onAccepted:
                        - type: linkedin_message
                          content: Thanks for accepting.
                          delay:
                            value: 2
                            unit: hours
                      onNotAccepted:
                        - type: task
                          taskType: TODO
                          title: Review no-response path
                          ownerId: owner-1
                    - type: email
                      subject: Following up
                      content: Continuing after the LinkedIn decision.
                      delay:
                        value: 1
                        unit: days
              voiceMessageWithVideoFollowup:
                summary: LinkedIn voice message followed by a video message
                value:
                  name: Voice + video outreach
                  identityId: 323
                  steps:
                    - type: linkedin_voice_message
                      content: >-
                        Hey {firstName}, sharing a quick voice intro about
                        {company}.
                      voiceId: bIHbv24MWmeRgasZH58o
                      voiceSettings:
                        voiceSpeed: 1
                        voiceStability: 80
                        backgroundNoise: none
                        backgroundVolume: 25
                    - type: linkedin_message
                      content: Following up with the demo video.
                      attachment: https://example.com/uploads/demo.mp4
                      attachmentName: demo.mp4
                      contentType: VIDEO
                      delay:
                        value: 1
                        unit: days
              taskRoutingFallbacks:
                summary: Route multiple fallback branches into different task owners
                value:
                  name: Task routing fallback flow
                  identityId: 323
                  steps:
                    - type: condition
                      condition:
                        type: has_professional_email
                      onTrue:
                        - type: email
                          subject: Initial outreach
                          content: Opening with email when contact data is ready.
                        - type: linkedin_connection
                          waitForAcceptance:
                            value: 4
                            unit: days
                          onAccepted:
                            - type: task
                              taskType: CALL
                              title: Call after positive signal
                              note: >-
                                They accepted the connection and saw the first
                                email.
                              ownerId: owner-1
                              delay:
                                value: 1
                                unit: days
                          onNotAccepted:
                            - type: task
                              taskType: TODO
                              title: Review cold branch manually
                              ownerId: owner-2
                      onFalse:
                        - type: task
                          taskType: TODO
                          title: Find email before launching sequence
                          ownerId: owner-3
      responses:
        '200':
          description: Campaign payload is valid against the public simplified schema
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      isValid:
                        type: boolean
                        enum:
                          - true
                        description: Always true for successful validation responses.
                      warnings:
                        type: array
                        items:
                          type: string
                        description: >-
                          Non-blocking sequence warnings returned by shared
                          campaign validation.
                    required:
                      - isValid
                      - warnings
                required:
                  - status
                  - message
                  - data
        '400':
          description: >-
            Bad request - the campaign payload does not match the public
            simplified schema.

            Common causes:

            - Referenced identity (`identityId`) does not exist in this
            workspace.

            - Unrecognized field on a step or branch (e.g. `delay` is not a
            valid key on conditional `onAccepted`/`onTrue`/`onFalse` branches —
            use a dedicated wait/delay node inside the branch instead).

            - Required field missing (the response `errors[].path` indicates
            which body location failed validation).
        '422':
          description: >-
            Unprocessable entity - the campaign steps are structurally invalid.

            Common causes the agent should fix before retrying:

            - EMAIL_OPENED / NOT_EMAIL_OPENED conditions may only appear *after*
            an EMAIL node in the same branch. Add the EMAIL node first or remove
            the condition.

            - Every conditional branch
            (`onAccepted`/`onNotAccepted`/`onTrue`/`onFalse`) must contain at
            least one node. Use `[{ "type": "end" }]` to terminate a branch
            explicitly instead of leaving it empty.

            - Templated fields (e.g. `{firstName}`, `{companyName}`) must
            reference AI variables or standard fields that exist for the
            campaign's entity. Use GET /v1/contacts/fields or GET
            /v1/companies/fields to discover valid placeholders before composing
            message bodies.

            - Reply emails inside the same thread may omit `subject`, but the
            first email in any thread must have a non-empty subject.
      security:
        - apiKey: []
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key

````