openapi: 3.0.3
info:
  title: MoEngage Campaigns API
  version: '2025-11-20'
  description: |
    The Campaigns V5 API manages the draft campaign lifecycle — draft creation, incremental updates, validation, test sends, and post-publish management.

    **Note:** Campaign publishing is not yet supported in V5. To publish campaigns, use the V1 API (`PATCH /core-services/v1/campaigns/{campaign_id}`) in the interim.

    **Supported channels:**
    - Push (Android, iOS, Web)
    - Email

    **Supported delivery types:**
    - ONE_TIME
    - PERIODIC
    - EVENT_TRIGGERED
    - BUSINESS_EVENT_TRIGGERED
    - DEVICE_TRIGGERED (Push only)
    - LOCATION_TRIGGERED (Push only)
    - BROADCAST_LIVE_ACTIVITY (Push iOS only)

    **Campaign lifecycle:**

    1. **Create** - Start a draft with only the required fields (`channel`, `campaign_delivery_type`, `created_by`). Add content, audience, and scheduling incrementally across subsequent update calls.
    2. **Update** - Patch individual components as you refine the setup. Each submitted component is validated in full before the draft is updated.
    3. **Validate** - Check whether a draft would pass publish-time validation without committing any changes.
    4. **Test** - Send a test message to specific users from either a saved draft or inline content before going live.
    5. **Manage** - Pause, resume, or stop a live campaign. Search your workspace and retrieve lightweight metadata across all campaigns.

    **Campaign versioning** is optional per workspace:
    - When enabled, publishing an update to a live campaign creates a new document with an incremented `version_number`.
    - `campaign_id` is the stable identifier across all versions; each version has its own raw `id` (ObjectId).

    **Authentication header:** The `MOE-APPKEY` request header is optional. Basic Auth already carries your Workspace ID as the username, so requests authenticate correctly without it.
  contact:
    name: MoEngage Developer Team
    email: support@moengage.com
    url: https://developers.moengage.com

servers:
  - url: 'https://api-{dc}.moengage.com/'
    description: MoEngage Campaigns API Server
    variables:
      dc:
        default: '01'
        description: 'Data center (DC) segment in the hostname. Replace `OX` with your workspace DC (01–06 or 101). See [Data centers](/api/introduction#data-centers).'

security:
  - BasicAuth: []

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: |
        Authentication is done via Basic Auth. This requires a base64-encoded string of your credentials in the format `username:password`.

        - **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
        - **Password**: Use an API key from **Settings** > **Account** > **API keys**.

        Refer to [API Key Dashboard](/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.
  
  parameters:
    MOE-APPKEY:
      name: MOE-APPKEY
      in: header
      required: false
      description: |
        Your MoEngage Workspace ID (App ID). Optional — Basic Auth already carries the Workspace ID as the username, so this header is not required. Find it in the dashboard at **Settings** > **Account** > **API keys**.
      schema:
        type: string
      example: "{{workspace_id}}"
    X-MOE-Request-Id:
      name: X-MOE-Request-Id
      in: header
      required: false
      description: |
        Client-supplied trace ID for tracing. Correlates with `response_id`. Supply this header or `request_id` in the body; if both are set, they must match.
      schema:
        type: string
    Idempotency-Key:
      name: Idempotency-Key
      in: header
      required: false
      description: |
        UUID v4. Required on all `POST` and `PATCH` requests except `POST /v5/campaigns/{campaign_id}/validate`. Repeating the same key returns the same response body.
      schema:
        type: string
        format: uuid
    Idempotency-Key-Required:
      name: Idempotency-Key
      in: header
      required: true
      description: |
        UUID v4. Required on all `POST` and `PATCH` requests except `POST /v5/campaigns/{campaign_id}/validate`. Repeating the same key returns the same response body.
      schema:
        type: string
        format: uuid

  schemas:
    # ==================== V5 Draft-First Envelopes ====================
    V5SuccessEnvelope:
      type: object
      properties:
        response_id: { type: string }
        type: { type: string, example: "campaign" }
        data: { type: object }
          
    V5ErrorEnvelope:
      type: object
      properties:
        response_id: { type: string }
        error:
          type: object
          properties:
            code: 
              type: string
              enum: [VALIDATION_FAILED, UNPROCESSABLE_ENTITY, BAD_REQUEST, RATE_LIMITED, UNAUTHORIZED, INTERNAL_ERROR, FORBIDDEN] 
            message: { type: string }
            target: { type: string }
            details:
              type: array
              items:
                type: object
                properties:
                  target: { type: string }
                  message: { type: string }
            request_id:
              type: string
              description: |
                The `request_id` from the originating request. Use this to correlate a failed response back to the specific call that triggered it, particularly useful in high-volume or retry scenarios.

                In V1, `request_id` appeared inside the `error` object. V5 preserves this field in the same location.

    # ==================== V5 Request Schemas ====================
    CampaignCreateV5Request:
      oneOf:
        - $ref: '#/components/schemas/PushCampaignCreateV5Request'
        - $ref: '#/components/schemas/EmailCampaignCreateV5Request'
      discriminator:
        propertyName: channel
        mapping:
          PUSH: '#/components/schemas/PushCampaignCreateV5Request'
          EMAIL: '#/components/schemas/EmailCampaignCreateV5Request'

    PushCampaignCreateV5Request:
      title: Push Campaign
      type: object
      description: |
        Request body for creating a Push campaign draft via `POST /v5/campaigns`.

        Only `channel`, `campaign_delivery_type`, and `created_by` are required. Optional components (`basic_details`, `campaign_content`, `segmentation_details`, and so on) can be included in the same request, or added later via `PATCH /v5/campaigns/{campaign_id}`.


        For full examples per delivery type, refer to `campaign_delivery_type` below. For the component-level schemas with conditional rules per template type, platform, and delivery type, refer to:
        - [Campaign content reference](/api/campaigns/campaign-content-reference) — `basic_details` and `campaign_content`.
        - [Audience and delivery reference](/api/campaigns/audience-scheduling-delivery-reference) — `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, `campaign_audience_limit`, `advanced`, and `geofences`.
      required: [channel, campaign_delivery_type, created_by]
      properties:
        request_id: 
          type: string
          description: |
            A unique identifier for this campaign creation request.
            
            **Important:** After successful campaign creation, do not reuse this request_id for the next 1 hour. If campaign creation fails, you can immediately retry with the same request_id.
          example: "{{request_id}}"
        channel: 
          type: string
          enum: [PUSH]
          description: The campaign channel. One of `PUSH` or `EMAIL`.
        campaign_delivery_type: 
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED]
          description: |
            The delivery type of the campaign.

            **Note:** `BROADCAST_LIVE_ACTIVITY` is not supported through the draft-based creation flow.

            For full request payloads per delivery type, see the code examples on this page.
        created_by: 
          type: string
          format: email
          description: The email ID of the user creating this campaign.
          example: "john.doe@example.com"
        basic_details: { $ref: '#/components/schemas/PushBasicDetailsV5' }
        trigger_condition: { $ref: '#/components/schemas/PushTriggerCondition' }
        campaign_content: { $ref: '#/components/schemas/PushCampaignContent' }
        segmentation_details: { $ref: '#/components/schemas/SegmentationDetails' }
        scheduling_details: { $ref: '#/components/schemas/SchedulingDetails' }
        delivery_controls: { $ref: '#/components/schemas/PushDeliveryControls' }
        advanced: { $ref: '#/components/schemas/AdvancedDetails' }
        conversion_goal_details: { $ref: '#/components/schemas/ConversionGoalDetails' }
        control_group_details: { $ref: '#/components/schemas/ControlGroupDetails' }
        utm_params: { $ref: '#/components/schemas/UTMParams' }
        campaign_audience_limit: { $ref: '#/components/schemas/CampaignAudienceLimit' }

    EmailCampaignCreateV5Request:
      title: Email Campaign
      type: object
      description: |
        Request body for creating an Email campaign draft via `POST /v5/campaigns`.

        Required fields are `channel`, `campaign_delivery_type`, and `created_by`. `connector` is required before the campaign can be published or sent for testing, but can be added later via `PATCH` (progressive creation). `campaign_content` can be added when the message content is ready.


        For the component-level schemas with conditional rules:
        - [Campaign content reference](/api/campaigns/campaign-content-reference) — `basic_details`, `campaign_content` (`html_content` and `custom_template_id`), `variation_details`, and `connector`.
        - [Audience and delivery reference](/api/campaigns/audience-scheduling-delivery-reference) — `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, and `campaign_audience_limit`.
      required: [channel, campaign_delivery_type, created_by]
      properties:
        request_id: 
          type: string
          description: |
            A unique identifier for this campaign creation request.
            
            **Important:** After successful campaign creation, do not reuse this request_id for the next 1 day. If campaign creation fails, you can immediately retry with the same request_id.
          example: "{{request_id}}"
        channel: 
          type: string
          enum: [EMAIL]
          description: The campaign channel. One of `PUSH` or `EMAIL`.
        campaign_delivery_type: 
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED]
          description: |
            The delivery type of the campaign.

            For full request payloads per delivery type, see the code examples on this page.
        created_by: 
          type: string
          format: email
          description: The email ID of the user creating this campaign.
          example: "john.doe@example.com"
        basic_details: { $ref: '#/components/schemas/EmailBasicDetailsV5' }
        trigger_condition: { $ref: '#/components/schemas/EmailTriggerCondition' }
        connector: { $ref: '#/components/schemas/Connector' }
        campaign_content: { $ref: '#/components/schemas/EmailCampaignContent' }
        segmentation_details: { $ref: '#/components/schemas/SegmentationDetails' }
        scheduling_details: { $ref: '#/components/schemas/SchedulingDetails' }
        delivery_controls: { $ref: '#/components/schemas/EmailDeliveryControls' }
        conversion_goal_details: { $ref: '#/components/schemas/ConversionGoalDetails' }
        control_group_details: { $ref: '#/components/schemas/ControlGroupDetails' }
        utm_params: { $ref: '#/components/schemas/UTMParams' }
        campaign_audience_limit: { $ref: '#/components/schemas/CampaignAudienceLimit' }

    CampaignPatchV5Request:
      description: Used for component-level edits on a campaign draft.
      oneOf:
        - $ref: '#/components/schemas/PushComponentPatchRequest'
        - $ref: '#/components/schemas/EmailComponentPatchRequest'

    PushComponentPatchRequest:
      title: Update Push Campaign Draft
      type: object
      description: |
        Update one or more components of a Push campaign draft. Only fields you include are changed,
        omitted fields retain their current values. When updating a nested field, include its complete
        parent object.

      properties:
        request_id:
          type: string
          description: A unique identifier for this update request.
          example: "{{request_id}}"
        channel:
          type: string
          enum: [PUSH]
          description: Must be `PUSH` for a Push update.
        campaign_delivery_type:
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED, BROADCAST_LIVE_ACTIVITY]
          description: |
            The delivery type of the campaign being updated.

            **Note:** `BROADCAST_LIVE_ACTIVITY` is included in this enum for campaigns created via the V1 API or legacy paths. Draft creation via `POST /v5/campaigns` does not support `BROADCAST_LIVE_ACTIVITY`. A draft cannot be transitioned to a Live Activity campaign through V5.
        updated_by:
          type: string
          format: email
          description: |
            The email address of the user making this update, used for audit trail purposes.
            If omitted, the update is attributed to the authenticated API credential.
          example: "marketer@example.com"
        basic_details:
          $ref: '#/components/schemas/PushBasicDetailsV5'
        trigger_condition:
          $ref: '#/components/schemas/PushTriggerCondition'
        campaign_content:
          $ref: '#/components/schemas/PushCampaignContent'
        segmentation_details:
          $ref: '#/components/schemas/SegmentationDetails'
        scheduling_details:
          $ref: '#/components/schemas/SchedulingDetails'
        delivery_controls:
          $ref: '#/components/schemas/PushDeliveryControls'
        advanced:
          $ref: '#/components/schemas/AdvancedDetails'
        conversion_goal_details:
          $ref: '#/components/schemas/ConversionGoalDetails'
        control_group_details:
          $ref: '#/components/schemas/ControlGroupDetails'
        utm_params:
          $ref: '#/components/schemas/UTMParams'
        campaign_audience_limit:
          allOf:
            - $ref: '#/components/schemas/CampaignAudienceLimit'
          description: |
            Configuration for capping how many users this campaign can reach. **Flag-gated feature** — must be enabled for your workspace by your MoEngage account team before use.

    EmailComponentPatchRequest:
      title: Update Email Campaign Draft
      type: object
      description: |
        Update one or more components of an Email campaign draft. Only fields you include are changed,
        omitted fields retain their current values. When updating a nested field, include its complete
        parent object.

      properties:
        request_id:
          type: string
          description: A unique identifier for this update request.
          example: "{{request_id}}"
        channel:
          type: string
          enum: [EMAIL]
          description: Must be `EMAIL` for an Email update.
        campaign_delivery_type:
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED]
          description: The delivery type of the campaign being updated.
        updated_by:
          type: string
          format: email
          description: |
            The email address of the user making this update, used for audit trail purposes.
            If omitted, the update is attributed to the authenticated API credential.
          example: "marketer@example.com"
        basic_details:
          $ref: '#/components/schemas/EmailBasicDetailsV5'
        connector:
          $ref: '#/components/schemas/Connector'
        trigger_condition:
          $ref: '#/components/schemas/EmailTriggerCondition'
        campaign_content:
          $ref: '#/components/schemas/EmailCampaignContent'
        segmentation_details:
          $ref: '#/components/schemas/SegmentationDetails'
        scheduling_details:
          $ref: '#/components/schemas/SchedulingDetails'
        delivery_controls:
          $ref: '#/components/schemas/EmailDeliveryControls'
        advanced:
          $ref: '#/components/schemas/AdvancedDetails'
        conversion_goal_details:
          $ref: '#/components/schemas/ConversionGoalDetails'
        control_group_details:
          $ref: '#/components/schemas/ControlGroupDetails'
        utm_params:
          $ref: '#/components/schemas/UTMParams'
        campaign_audience_limit:
          allOf:
            - $ref: '#/components/schemas/CampaignAudienceLimit'
          description: |
            Configuration for capping how many users this campaign can reach. **Flag-gated feature** — must be enabled for your workspace by your MoEngage account team before use.

    PublishCampaignRequest:
      title: Publish Campaign Patch
      type: object
      required: [status]
      properties:
        request_id: 
          type: string
          description: A unique identifier for this update request.
        status:
          type: string
          description: |
            Reserved for future use. Campaign publishing is not yet supported in V5. Do not set this field.


    CampaignStatusV5Request:
      type: object
      description: Changes the status of a single published campaign. One campaign ID per request.
      required: [action]
      properties:
        request_id:
          type: string
          description: |
            A client-supplied identifier for this status change request, echoed back as `response_id` so you can correlate the request and response. This is not a deduplication key — to make a request idempotent, use the `Idempotency-Key` header. Replaying the same `Idempotency-Key` returns the original response.
        action:
          type: string
          enum: [STOP, PAUSE, RESUME]
          description: |
            Lifecycle action for an already published or scheduled campaign.

            Each action applies only to specific delivery types and requires the campaign to be in a valid source state:

            | Action | Supported delivery types | Valid source states |
            | :--- | :--- | :--- |
            | `STOP` | `ONE_TIME` | `ACTIVE`, `SCHEDULED`, `PAUSED`, `SENDING` |
            | `PAUSE` | `PERIODIC`, `EVENT_TRIGGERED` | `ACTIVE`, `SCHEDULED`, `SENDING` |
            | `RESUME` | `PERIODIC`, `EVENT_TRIGGERED` | `PAUSED` |

            <Note>
             `STOP` cannot be used on Periodic campaigns. `PAUSE` and `RESUME` cannot be used on One-time campaigns.
            </Note>


    SearchV5Request:
      type: object
      required: [limit, page]
      properties:
        request_id:
          type: string
          description: |
            An optional unique identifier for this search request (UUID v4 recommended).

            - In V1, `request_id` was required on every search call. In V5, it is optional.
            - Providing it is recommended for traceability and debugging. MoEngage support can use the value to locate the specific request in server logs.
          example: "{{request_id}}"
        campaign_fields: { $ref: '#/components/schemas/CampaignSearchFieldsV5' }
        include_child_campaigns:
          type: boolean
          default: false
          description: |
            When `true`, includes child campaigns in results. Defaults to `false`.

            Child campaign types included:
            - **Periodic children:** Carry `parent_id` in the response.
            - **Flow-node campaigns:** Carry `flow_id` and `flow_name` in the response.

        include_archive_campaigns:
          type: boolean
          default: false
          description: |
            When `true`, includes campaigns in `ARCHIVED` status in search results.
            By default, archived campaigns are excluded even if `ARCHIVED` is not in
            `campaign_fields.status`.

            Defaults to `false`.

        limit:
          type: integer
          description: |
            The number of campaigns to return per page.

            **Maximum:** 15. The response does not include a `total_count` field - to determine
            the total number of results, keep paginating until a response returns fewer items
            than `limit`.
          minimum: 1
          maximum: 15
          example: 10
        page:
          type: integer
          description: |
            The page number to retrieve (1-indexed).

            For example, with 200 campaigns and a limit of 10, there are 20 pages.
          minimum: 1
          example: 1
          
    CampaignSearchFieldsV5:
      type: object
      description: |
        Filter criteria for searching campaigns.

        The following filter field names changed from V1 to V5:
        - `id` (V1, string) is `ids` in V5 (array of strings). Pass a single-element array to replicate V1 single-ID filtering.
        - `delivery_type` (V1) is `campaign_delivery_type` in V5.

        All other V1 filter fields (`channels`, `created_by`, `created_date`, `name`, `status`, `tags`) use the same names in V5.
      properties:
        ids:
          type: array
          items:
            type: string
          description: |
            Filter by one or more campaign IDs.

            In V1, this field was `id` (a single string). In V5, it is `ids` (an array). For single-campaign retrieval, `GET /v5/campaigns/{campaign_id}` is the preferred endpoint.

          example: ["64a1b2c3d4e5f6a7b8c9d0e1"]
        channels:
          type: array
          items:
            type: string
            enum: [EMAIL, PUSH]
          description: Include campaigns for any of these channels.
          example: ["PUSH", "EMAIL"]
        created_by:
          type: array
          items:
            type: string
            format: email
          description: Include campaigns created by any of these users (email addresses).
          example: ["marketer@example.com"]
        created_date:
          type: object
          description: Filter campaigns by creation date range.
          properties:
            from_date:
              type: string
              format: date-time
              description: Start of the creation date range (ISO 8601 format).
              example: "2024-01-01T00:00:00"
            to_date:
              type: string
              format: date-time
              description: End of the creation date range (ISO 8601 format).
              example: "2024-12-31T23:59:59"
        campaign_delivery_type:
          type: array
          items:
            type: string
            enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED]
          description: |
            Include campaigns with any of these delivery types.

            In V1, this field was `delivery_type`. In V5, it is `campaign_delivery_type`.
          example: ["ONE_TIME", "PERIODIC"]
        name:
          type: string
          description: Filter campaigns by name. Partial matches are supported.
          example: "Summer Sale"
        status:
          type: array
          items:
            type: string
            enum: [DRAFT, ACTIVE, SCHEDULED, PAUSED, SENT, SENDING, STOPPED, ARCHIVED]
          description: |
            Include campaigns in any of these statuses.

            Drafts are only returned when `DRAFT` is explicitly included here.

        tags:
          type: array
          items:
            type: string
          description: Include campaigns that have any of these tags.
          example: ["promotional", "seasonal"]
        version_number:
          type: integer
          description: |
            Filter by campaign version. Only applies when campaign versioning is enabled for your workspace. Omit to include all versions.


    MetaV5Request:
      type: object
      required: [limit, page]
      properties:
        request_id:
          type: string
          description: A unique identifier for this metadata retrieval request.
          example: "{{request_id}}"
        campaign_fields: { $ref: '#/components/schemas/CampaignMetaSearchFieldsV5' }
        include_child_campaigns:
          type: boolean
          default: false
          description: |
            When `true`, returns child campaign metadata for periodic campaigns.
            
            Activates the following response fields on each campaign object:
            - `total_child_campaigns` - number of child campaigns (present on the parent campaign).
            - `parent_campaign_id` - the parent campaign's ID (present on each child campaign).
            
            Defaults to `false`.
        limit: 
          type: integer
          description: |
            The number of campaigns to display per page.
            
            **Maximum:** 15. The response does not include a `total_count` field - to determine the total number of results, keep paginating until a response returns fewer items than `limit`.
          minimum: 1
          maximum: 15
          example: 15
        page: 
          type: integer
          description: |
            The page number to retrieve.
            
            For example, if there are 200 campaigns and the limit is 10, there will be 20 pages.
          minimum: 1
          example: 1

    CampaignMetaSearchFieldsV5:
      type: object
      description: Filter criteria for retrieving campaign metadata.
      properties:
        id:
          type: string
          description: |
            The unique identifier of the campaign to retrieve.

          example: "{{campaign_id}}"
        channels:
          type: array
          items:
            type: string
            enum: [EMAIL, PUSH, SMS, WHATSAPP, FACEBOOK, 'GOOGLE ADS', CONNECTORS]
          description: |
            Include campaigns for any of these channels.

          example: ["PUSH", "EMAIL"]
        created_by:
          type: array
          items:
            type: string
            format: email
          description: Include campaigns created by any of these users (email addresses).
          example: ["user@example.com"]
        created_date:
          type: object
          description: Filter campaigns by creation date range.
          properties:
            from_date:
              type: string
              format: date-time
              description: Start of the creation date range (ISO 8601 format).
              example: "2024-01-01T00:00:00"
            to_date:
              type: string
              format: date-time
              description: End of the creation date range (ISO 8601 format).
              example: "2024-12-31T23:59:59"
        delivery_type:
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED]
          description: |
            Filter campaigns by delivery type.

        name:
          type: string
          description: Filter campaigns by name. Partial matches are supported.
          example: "Summer Sale"
        status:
          type: array
          items:
            type: string
            enum: [DRAFT, SCHEDULED, ACTIVE, PAUSED, SENT, SENDING, STOPPED, ARCHIVED]
          description: Include campaigns in any of these statuses.
        tags:
          type: array
          items:
            type: string
          description: Include campaigns that have any of these tags.
          example: ["promotional", "seasonal"]

    TestV5Request:
      oneOf:
        - $ref: '#/components/schemas/TestV5PushRequest'
        - $ref: '#/components/schemas/TestV5EmailRequest'
      discriminator:
        propertyName: channel
        mapping:
          PUSH: '#/components/schemas/TestV5PushRequest'
          EMAIL: '#/components/schemas/TestV5EmailRequest'

    TestV5PushRequest:
      title: Push
      type: object
      description: |
        Push test request. Supports two modes:

        - **Inline mode**: Supply `channel` and `campaign_content` directly. Nothing is stored on the server.
        - **Draft mode**: Supply `draft_id` to load content from a saved DRAFT campaign. By default, the server sends one test per platform, locale, and variation defined in the draft. Use `test_campaign_meta.platform`, `locale_name`, or `variation` to narrow the send.

        **Conditional required fields:** `test_campaign_meta` is always required. Inline mode additionally requires `channel` and `campaign_content`. Draft mode additionally requires `draft_id`. These per-mode requirements are enforced by the API but cannot be expressed in the `required` array, which lists only the always-required `test_campaign_meta`.
      required: [test_campaign_meta]
      properties:
        request_id:
          type: string
          description: Optional unique identifier for this test request (UUID v4 recommended). Used for idempotency.
          example: "{{request_id}}"
        channel:
          type: string
          enum: [PUSH]
          description: Required for inline mode. Must be `PUSH`.
        draft_id:
          type: string
          description: >-
            Required for draft mode. Raw 24-character ObjectId of the saved DRAFT campaign to test.
            Obtain this value from the `id` returned when the draft was created. Mutually exclusive with `channel` and `campaign_content`.
          example: "{{campaign_id}}"
        basic_details:
          $ref: '#/components/schemas/PushBasicDetailsV5'
        campaign_content:
          $ref: '#/components/schemas/PushCampaignContent'
        personalization_details:
          $ref: '#/components/schemas/PersonalizationDetails'
        test_campaign_meta:
          $ref: '#/components/schemas/TestCampaignMetaV5'

    TestV5EmailRequest:
      title: Email
      type: object
      description: |
        Email test request. Supports two modes:

        - **Inline mode**: Supply `channel`, `basic_details`, `connector`, and `campaign_content` directly. Nothing is stored on the server.
        - **Draft mode**: Supply `draft_id` to load content from a saved DRAFT campaign. By default, the server sends one test per locale and variation defined in the draft. Use `test_campaign_meta.locale_name` or `variation` to narrow the send.

        **Conditional required fields:** `test_campaign_meta` is always required. Inline mode additionally requires `channel`, `basic_details`, `connector`, and `campaign_content`. Draft mode additionally requires `draft_id`. These per-mode requirements are enforced by the API but cannot be expressed in the `required` array, which lists only the always-required `test_campaign_meta`.
      required: [test_campaign_meta]
      properties:
        request_id:
          type: string
          description: Optional unique identifier for this test request (UUID v4 recommended). Used for idempotency.
          example: "{{request_id}}"
        channel:
          type: string
          enum: [EMAIL]
          description: Required for inline mode. Must be `EMAIL`.
        draft_id:
          type: string
          description: >-
            Required for draft mode. Raw 24-character ObjectId of the saved DRAFT campaign to test.
            Obtain this value from the `id` returned when the draft was created. Mutually exclusive with `channel` and `campaign_content`.
          example: "{{campaign_id}}"
        basic_details:
          $ref: '#/components/schemas/EmailBasicDetailsV5'
        connector:
          $ref: '#/components/schemas/Connector'
        campaign_content:
          $ref: '#/components/schemas/EmailCampaignContent'
        personalization_details:
          $ref: '#/components/schemas/PersonalizationDetails'
        test_campaign_meta:
          $ref: '#/components/schemas/TestCampaignMetaV5'

    PersonalizationDetails:
      type: object
      description: |
        Overrides user and event attribute values used to render personalised content during the test send.
        When supplied, these values replace what would normally be fetched from the stored user profile,
        useful for testing personalised content without needing a matching user record in MoEngage.

        <details>
        <summary>Test with personalization overrides</summary>

        ```json
        {
          "draft_id": "{{campaign_id}}",
          "personalization_details": {
            "user_attributes": {
              "First Name": "{{first_name}}",
              "City": "{{city}}"
            },
            "event_attributes": {
              "product_name": "{{product_name}}",
              "price": 79.99
            }
          },
          "test_campaign_meta": {
            "identifier": "USER_ATTRIBUTE_UNIQUE_ID",
            "identifier_values": [
              "{{user_unique_id}}"
            ]
          }
        }
        ```

        </details>
      properties:
        user_attributes:
          type: object
          description: Key-value pairs of user attribute names and the override values to use for this test send.
          example:
            First Name: "Jane"
            City: "New York"
        event_attributes:
          type: object
          description: Key-value pairs of event attribute names and the override values to use for this test send.
          example:
            product_name: "Running Shoes"
            price: 79.99

    TestCampaignMetaV5:
      type: object
      description: Test audience and delivery options.
      required: [identifier, identifier_values]
      properties:
        identifier:
          type: string
          enum:
            - USER_ATTRIBUTE_UNIQUE_ID
            - USER_ATTRIBUTE_USER_EMAIL
            - USER_ATTRIBUTE_USER_MOBILE
            - MOE_GAID
            - ADVERTISING_IDENTIFIER
            - PUSH_ID
            - CUSTOM_SEGMENT
            - EMAIL
            - ID
            - MOBILE_NUMBER
          description: |
            The type of identifier used to target test recipients. Supported values vary by channel:

            **Push campaigns:**
            - `USER_ATTRIBUTE_UNIQUE_ID` - Permanent internal user ID (for example, `USER_1138`).
            - `USER_ATTRIBUTE_USER_EMAIL` - User's email address (for example, `jane@example.com`).
            - `USER_ATTRIBUTE_USER_MOBILE` - User's mobile number in E.164 format (for example, `+14155550101`).
            - `MOE_GAID` - Google Advertising ID (Android devices).
            - `ADVERTISING_IDENTIFIER` - Resettable advertising ID (GAID on Android, IDFA on iOS).
            - `PUSH_ID` - Unique device push token.
            - `CUSTOM_SEGMENT` - User-defined segment.

            **Email campaigns:**
            - `EMAIL` - Email address. Content will not be personalised with user profile data if no matching record exists in MoEngage.
            - `ID` - MoEngage internal user ID.
            - `MOBILE_NUMBER` - User's mobile number in E.164 format.
            - `CUSTOM_SEGMENT` - User-defined segment.

            <details>
            <summary>USER_ATTRIBUTE_UNIQUE_ID</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "USER_ATTRIBUTE_UNIQUE_ID",
                "identifier_values": [
                  "{{user_unique_id}}"
                ]
              }
            }
            ```

            </details>
            <details>
            <summary>EMAIL</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "EMAIL",
                "identifier_values": [
                  "{{recipient_email}}"
                ]
              }
            }
            ```

            </details>
            <details>
            <summary>MOE_GAID (Android)</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "MOE_GAID",
                "identifier_values": [
                  "{{moe_gaid}}"
                ]
              }
            }
            ```

            </details>
            <details>
            <summary>PUSH_ID</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "PUSH_ID",
                "identifier_values": [
                  "{{push_id}}"
                ]
              }
            }
            ```

            </details>
            <details>
            <summary>CUSTOM_SEGMENT</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "CUSTOM_SEGMENT",
                "identifier_values": [
                  "{{custom_segment_name}}"
                ]
              }
            }
            ```

            </details>
        identifier_values:
          type: array
          items: { type: string }
          maxItems: 10
          description: Array of identifier values to send the test campaign to. Maximum 10 values per request.
          example: ["{{identifier_value}}"]
        variation:
          type: string
          description: |
            A/B variation name to target for this test send (for example, `variation_1`, `control`).
            If omitted, the server sends one test per variation defined in the draft.

            <details>
            <summary>Target a single A/B variation</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "USER_ATTRIBUTE_UNIQUE_ID",
                "identifier_values": [
                  "{{user_unique_id}}"
                ],
                "variation": "variation_2"
              }
            }
            ```

            </details>
          example: "{{variation_name}}"
        locale_name:
          type: string
          description: |
            Locale name to target for this test send (for example, `en`, `fr`).
            If omitted, the server sends one test per locale defined in the draft.

            <details>
            <summary>Target a single locale</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "USER_ATTRIBUTE_UNIQUE_ID",
                "identifier_values": [
                  "{{user_unique_id}}"
                ],
                "locale_name": "es-ES"
              }
            }
            ```

            </details>
          example: "{{locale_name}}"
        platform:
          type: string
          enum: [ANDROID, IOS, WEB]
          description: |
            Optional. Applies to draft mode only. Narrows the test send to a single platform.
            If omitted, the server sends one test per platform defined in the draft.

            <details>
            <summary>Target a single platform (iOS only)</summary>

            ```json
            {
              "draft_id": "{{campaign_id}}",
              "test_campaign_meta": {
                "identifier": "USER_ATTRIBUTE_UNIQUE_ID",
                "identifier_values": [
                  "{{user_unique_id}}"
                ],
                "platform": "IOS"
              }
            }
            ```

            </details>

    # ==================== Shared V5/V1 Components ====================
    PushBasicDetailsV5:
      type: object
      description: |
        Identifying metadata for the Push campaign, including name, team, tags, and platform targeting.

        For field-by-field rules, conditional requirements, and platform-specific delivery flags (Android `push_amp_plus_enabled`, iOS provisional-push audience flags), refer to [Push campaign metadata](/api/campaigns/campaign-content-reference#push-campaign-metadata).
      properties:
        name: 
          type: string
          description: The name of the campaign.
          example: "Summer Sale Push Notification"
        business_event: 
          type: string
          description: |
            The business event to be mapped to the campaign.
            
            **Required** for BUSINESS_EVENT_TRIGGERED campaigns.
          example: "user_signup"
        tags: 
          type: array
          items: { type: string }
          description: Tags that provide context about the campaign's nature or theme.
          example: ["activation", "summer_sale"]
        team: 
          type: string
          description: |
            The name of the team collaborating on this campaign.
            For more information, refer to [Teams in MoEngage](/user-guide/settings/account/team-management/teams-in-moengage).
          example: "marketing_team"
        platforms: 
          type: array
          items: { type: string, enum: [ANDROID, IOS, WEB] }
          description: The platforms to target for this Push campaign.
          example: ["ANDROID", "IOS"]
        broadcast_live_activity_id:
          type: string
          description: |
            The broadcast live activity ID for iOS Live Activities.

            **Required** when platform is iOS and delivery_type is BROADCAST_LIVE_ACTIVITY.

            **Not applicable in the draft-based creation flow.** `BROADCAST_LIVE_ACTIVITY` is not supported via POST `/v5/campaigns`.
          example: "live_check123"
        geofences: { $ref: '#/components/schemas/Geofences' }
        send_to_triggered_platform_only: 
          type: boolean
          description: Whether to send the campaign only to the platform that triggered the event. Applicable for event-triggered campaigns.
        platform_specific_details: { $ref: '#/components/schemas/PlatformSpecificDetails' }
        
    EmailBasicDetailsV5:
      type: object
      description: |
        Identifying metadata for the Email campaign, including name, team, tags, and subscription category.

        **Conditional requirements:** `name`, `content_type`, `user_attribute_identifier`, and `subscription_category` are strictly required only when using the `/v5/campaigns/test` endpoint in **inline mode**. They are not required at campaign creation time (V5 supports progressive creation, where components are added later via PATCH) and are not needed in **draft mode** tests, where content comes from the saved draft.
      properties:
        name: 
          type: string
          description: Any string name for the test or campaign.
          example: "Summer Sale Email"
        business_event: 
          type: string
          description: The business event to be mapped to the campaign.
          example: "user_signup"
        content_type: 
          type: string
          enum: [PROMOTIONAL, TRANSACTIONAL]
          description: The type of content in the campaign. "PROMOTIONAL" or "TRANSACTIONAL".
        subscription_category: 
          type: string
          description: |
            The subscription category for promotional email campaigns. **Must match a valid category configured in your workspace.**
            
            **Required** for PROMOTIONAL email campaigns and inline tests.
          example: "marketing"
        tags: 
          type: array
          items: { type: string }
          description: Tags that provide context about the campaign's nature or theme.
          example: ["activation", "summer_sale"]
        team: 
          type: string
          description: The name of the team collaborating on this campaign.
          example: "marketing_team"
        send_only_double_opt_in_users:
          type: boolean
          description: |
            Restricts the campaign to users who have double-opted in. Applies only to `PROMOTIONAL` campaigns.

            For the validation errors returned when this field is misused, refer to the `400` response examples on the Create, Update, and Validate Campaign endpoints.
          example: true
        deduplication_attribute:
          type: string
          description: |
            Brand-level deduplication. A user matching this attribute receives at most one send across campaigns that share the same value.

            - Pass `""` (empty string) for single-brand use (no dedup).
            - Pass a user attribute name for multi-brand dedup. Use the backend attribute name (for example, `u_em`), not the readable/display name (for example, `Email (Standard)`) — the same requirement as the segmentation component elsewhere in this API.
            - `deduplication_attribute` is not supported for `API_TRIGGERED` campaigns. Passing this field on an API-triggered campaign returns a 400 validation error.

            For the validation errors returned when this field is misused, refer to the `400` response examples on the Create, Update, and Validate Campaign endpoints.
          example: "u_em"
        user_attribute_identifier: 
          type: string
          default: "Email (Standard)"
          description: |
            The user attribute that stores the recipient email address. Use "Email (Standard)" for email channels.
          example: "Email (Standard)"

    PlatformSpecificDetails:
      type: object
      description: |
        Platform-specific configuration details for Push.

        For runnable Android and iOS examples and the mutual-exclusion rule on iOS audience flags, refer to [Platform-specific delivery flags](/api/campaigns/campaign-content-reference#platform-specific-delivery-flags).
      properties:
        android:
          type: object
          properties:
            push_amp_plus_enabled: 
              type: boolean
              default: false
              description: Whether Push Amp+ feature is enabled for this campaign.
        ios:
          type: object
          description: |
            **Note:** You must pass one of these keys as true for iOS.

          properties:
            send_to_all_eligible_device: 
              type: boolean
              description: Whether to send the campaign to all eligible devices.
            exclude_provisional_push_devices: 
              type: boolean
              description: Whether to exclude provisional push devices.
            send_to_only_provisional_push_enabled_devices: 
              type: boolean
              description: Whether to send only to provisional push-enabled devices.

    Geofences:
      type: object
      description: |
        Geofence location details for location-triggered campaigns.

        **Required** for LOCATION_TRIGGERED campaigns.

        For per-`triggered_at` runnable payloads (`ENTRY`, `EXIT`, `dwell`) and the casing distinction between `ENTRY`/`EXIT` (uppercase) and `dwell` (lowercase), refer to [Geofence targeting](/api/campaigns/audience-scheduling-delivery-reference#geofence-targeting).

      required: [name, latitude, longitude, radius, response_time_value, response_time_granularity, triggered_at]
      properties:
        name: 
          type: string
          description: The unique name of the geofence location being targeted.
        latitude: 
          type: string
          description: The latitude coordinate for the center of the geofence area.
        longitude: 
          type: string
          description: The longitude coordinate for the center of the geofence area.
        radius: 
          type: string
          description: The radius in meters from the center point that defines the boundary of the geofence.
        dwell_time_value: 
          type: string
          description: |
            The numeric value for the time to wait before sending the message after the trigger condition is met.
            
            **Required** when triggered_at is set to "dwell".
        dwell_time_granularity: 
          type: string
          enum: [MINUTES, HOURS, DAYS]
          description: |
            The time unit for the dwell_time_value.
            
            **Required** when triggered_at is set to "dwell".
        response_time_value: 
          type: string
          description: The numeric value for the time to wait before sending the message after the trigger condition is met.
        response_time_granularity: 
          type: string
          enum: [MINUTES, HOURS, DAYS]
          description: The time unit for the response_time_value.
        triggered_at: 
          type: string
          enum: [ENTRY, EXIT, dwell]
          description: |
            The user action that triggers the campaign (when user enters/exits the geofence).


    PushTriggerCondition:
      type: object
      description: |
        Trigger condition details for Push event-triggered, device-triggered, and related campaigns.

        **Required** for `EVENT_TRIGGERED`, `DEVICE_TRIGGERED`, and `LOCATION_TRIGGERED` Push campaigns.

        For per-delay-type runnable payloads (`ASAP`, `DELAY` with `AFTER`/`BEFORE`, `INTELLIGENT_DELAY`), filter primitives, and primary/secondary filter combinations, refer to [Trigger conditions](/api/campaigns/audience-scheduling-delivery-reference#trigger-conditions).

      properties:
        included_filters: { $ref: '#/components/schemas/FilterGroup' }
        secondary_included_filters:
          allOf:
            - $ref: '#/components/schemas/FilterGroup'
          description: Additional filters that must also be satisfied for the trigger to fire. For runnable examples, refer to [Primary and secondary trigger filters](/api/campaigns/audience-scheduling-delivery-reference#primary-and-secondary-trigger-filters).
        trigger_delay_type:
          type: string
          enum: [DELAY, ASAP, INTELLIGENT_DELAY]
          description: |
            The type of triggered delay.
            
            When set to DELAY, the following fields are mandatory:
            - trigger_delay_value
            - trigger_delay_granularity
            - trigger_relation
        trigger_delay_value: 
          type: integer
          minimum: 0
          description: The numeric value of the triggered delay.
        trigger_delay_granularity: 
          type: string
          enum: [MINUTES, HOURS, DAYS]
          description: The time unit for the trigger delay.
        trigger_relation: 
          type: string
          enum: [BEFORE, AFTER]
          description: |
            The trigger relation with delay.
            
            **Required** when trigger_delay_type is DELAY.

        trigger_attr:
          type: string
          description: The attribute value of the trigger. Pass the string `"If Action"` for event-triggered campaigns.
        intelligent_delay_optimization:
          type: object
          description: |
            Configuration for intelligent delay optimization.
            
            Used when trigger_delay_type is INTELLIGENT_DELAY. Defines a time window (min/max delay) within which the system finds the optimal moment to send the message.
          properties:
            min_delay_value: 
              type: integer
              description: The numeric component of the lower bound for the intelligent delay window.
            min_delay_granularity: 
              type: string
              enum: [MINUTES, HOURS]
              description: The time unit that qualifies the min_delay_value.
            max_delay_value: 
              type: integer
              description: The numeric component of the upper bound for the intelligent delay window.
            max_delay_granularity: 
              type: string
              enum: [HOURS, DAYS]
              description: The time unit that qualifies the max_delay_value.

    EmailTriggerCondition:
      type: object
      description: |
        Trigger condition details for Email event-triggered campaigns.

        **Required** for EVENT_TRIGGERED campaigns.

        For per-delay-type runnable payloads (`ASAP`, `DELAY` with `AFTER`/`BEFORE`), filter primitives, and primary/secondary filter combinations, refer to [Trigger conditions](/api/campaigns/audience-scheduling-delivery-reference#trigger-conditions).

      properties:
        included_filters: { $ref: '#/components/schemas/FilterGroup' }
        secondary_included_filters:
          allOf:
            - $ref: '#/components/schemas/FilterGroup'
          description: Additional filters that must also be satisfied for the Email trigger to fire. For runnable examples, refer to [Primary and secondary trigger filters](/api/campaigns/audience-scheduling-delivery-reference#primary-and-secondary-trigger-filters).
        trigger_delay_type: 
          type: string
          enum: [DELAY, ASAP]
          description: |
            The type of triggered delay.
            
            When set to DELAY, the following fields are mandatory:
            - trigger_delay_value
            - trigger_delay_granularity
            - trigger_relation
        trigger_delay_value: 
          type: integer
          minimum: 0
          description: The numeric value of the triggered delay.
        trigger_delay_granularity: 
          type: string
          enum: [MINUTES, HOURS, DAYS]
          description: The time unit for the trigger delay.
        trigger_relation: 
          type: string
          enum: [BEFORE, AFTER]
          description: |
            The trigger relation with delay.
            
            **Required** when trigger_delay_type is DELAY.

        trigger_attr:
          type: string
          description: The attribute value of the trigger. Pass the string `"If Action"` for event-triggered campaigns.

    PushCampaignContent:
      type: object
      description: |
        Push message content, locales, and A/B variations.

        For full `POST /v5/campaigns` request examples that include `campaign_content`, refer to `campaign_delivery_type` on `PushCampaignCreateV5Request`, `VariationDetails`, and the Create Campaign Draft code samples.

        For per-template-type runnable payloads, refer to [Android push content](/api/campaigns/campaign-content-reference#android-push-content), [iOS push content](/api/campaigns/campaign-content-reference#ios-push-content), or [Web push content](/api/campaigns/campaign-content-reference#web-push-content).
      properties:
        locales: 
          type: array
          items: { type: string }
          description: |
            Additional locale names for multi-language campaigns. List only the non-default locales here (for example, `"en-US"`, `"es-ES"`). The `"default"` locale is always implicitly present and must not be included in this array.
          example: ["en-US", "es-ES"]
        variation_details: { $ref: '#/components/schemas/VariationDetails' }
        content:
          type: object
          description: |
            The campaign message payload. Two shapes are accepted:

            - **Flat shape (no locales or variations):** Pass a flat `push` object directly under `content`.
            - **Locale-keyed and variation-keyed shape:** Key by locale name, then by variation name: `content[locale_name][variation_name] = { push: { ... } }`. The `"default"` locale key is always required and serves as the fallback. Additional locale keys must match values listed in `locales`.
            <Warning>
            If your V1 integration used locale-variation wrapping, you must continue using that structure in V5. The flat format is only valid when no locales or A/B variations are configured on the campaign.
            </Warning>

            For runnable payloads for both shapes, refer to [Content payload structure](/api/campaigns/campaign-content-reference#content-payload-structure).
          properties:
            push: { $ref: '#/components/schemas/PushContent' }
          additionalProperties:
            type: object
            description: Variation-keyed content blocks for a single locale.
            additionalProperties:
              type: object
              properties:
                push: { $ref: '#/components/schemas/PushContent' }

    EmailCampaignContent:
      type: object
      description: |
        Email content, locales, and A/B variations.

        For full `POST /v5/campaigns` request examples that include `campaign_content`, refer to `campaign_delivery_type` on `EmailCampaignCreateV5Request`, `VariationDetails`, and the Create Campaign Draft code samples.

        For Email content variants, including `html_content` versus `custom_template_id`, CC/BCC, attachments, and editor selection (`Froala Editor` versus `Ace Editor`), refer to [Email content](/api/campaigns/campaign-content-reference#email-content).
      properties:
        locales: 
          type: array
          items: { type: string }
          description: |
            Additional locale names for multi-language campaigns. List only the non-default locales here (for example, `"en-US"`, `"es-ES"`). The `"default"` locale is always implicitly present and must not be included in this array.
          example: ["en-US", "es-ES"]
        variation_details: { $ref: '#/components/schemas/VariationDetails' }
        content:
          type: object
          description: |
            The campaign message payload. Two shapes are accepted:

            - **Flat shape (no locales or variations):** Pass a flat `email` object directly under `content`.
            - **Locale-keyed and variation-keyed shape:** Key by locale name, then by variation name: `content[locale_name][variation_name] = { email: { ... } }`. The `"default"` locale key is always required and serves as the fallback. Additional locale keys must match values listed in `locales`.

            For runnable payloads for both shapes, refer to [Content payload structure](/api/campaigns/campaign-content-reference#content-payload-structure).
          properties:
            email: { $ref: '#/components/schemas/EmailContent' }
          additionalProperties:
            type: object
            description: Variation-keyed content blocks for a single locale.
            additionalProperties:
              type: object
              properties:
                email: { $ref: '#/components/schemas/EmailContent' }

    VariationDetails:
      type: object
      description: |
        Configuration for A/B testing variations.

        For runnable `MANUAL` (fixed split) and `SHERPA` (auto-optimized) examples, refer to [A/B test variations](/api/campaigns/campaign-content-reference#a-b-test-variations).
      properties:
        distribution_type: 
          type: string
          enum: [SHERPA, MANUAL]
          description: |
            The traffic distribution method for A/B test variations.

            - `MANUAL` - you specify a fixed percentage split across variations.
            - `SHERPA` - MoEngage's AI-powered optimizer automatically shifts traffic toward the best-performing variation during the campaign run.
        no_of_variations: 
          type: integer
          minimum: 1
          description: The number of A/B test variations.
          example: 2
        manual_distribution_percentage: 
          type: object
          additionalProperties: { type: integer }
          description: |
            Fixed percentage of audience assigned to each variation. 
            **Note:** Variation keys must follow the `variation_N` naming format (e.g. `variation_1`, `variation_2`). Keys with any other format like `var_1`, `1`, `2` will be rejected with a validation error.

            **Required** when `distribution_type` is `MANUAL`.
          example:
            variation_1: 50
            variation_2: 50
        sherpa_campaign_duration: 
          type: integer
          description: |
            Duration in hours over which MoEngage Sherpa collects performance data before declaring a winning variation.

            **Required** when `distribution_type` is `SHERPA`.
        sherpa_distribution_metric: 
          type: string
          enum: [OPEN_RATE, CLICK_RATE, BOTH]
          description: |
            The engagement metric Sherpa uses to evaluate and rank variations.

            **Required** when `distribution_type` is `SHERPA`.

    PushContent:
      type: object
      description: Push notification content for Android, iOS, and Web platforms.
      properties:
        android: { $ref: '#/components/schemas/AndroidPushContent' }
        ios: { $ref: '#/components/schemas/IOSPushContent' }
        web: { $ref: '#/components/schemas/WebPushContent' }

    AndroidPushContent:
      type: object
      description: Android push notification content.
      properties:
        template_type: 
          type: string
          enum: [BASIC, STYLIZED_BASIC, SIMPLE_IMAGE_CAROUSEL, IMAGE_BANNER_WITH_TEXT, TIMER, TIMER_WITH_PROGRESS_BAR, Custom]
          description: |
            The type of Android push template.
            
            **Note:** If you are passing a template ID, set template_type to "Custom" and use `custom_template_id`.

        custom_template_id: 
          type: string
          description: |
            The ID of the custom template.
            
            **Required** when template_type is "Custom".
        custom_template_version: 
          type: integer
          description: The version of the custom template.
        basic_details: { $ref: '#/components/schemas/AndroidBasicDetails' }
        timer: { $ref: '#/components/schemas/AndroidTimer' }
        buttons: 
          type: array
          items: { $ref: '#/components/schemas/AndroidButton' }
          description: Action buttons for the notification.
        advanced: { $ref: '#/components/schemas/AndroidAdvanced' }
        template_backup: { $ref: '#/components/schemas/AndroidTemplateBackup' }

    AndroidBasicDetails:
      type: object
      description: |
        Basic details for the Android push notification. 
        
        Fields vary by template_type. All templates support common fields like title, message, default_click_action.
      properties:
        notification_channel: 
          type: string
          description: The Android notification channel where the push will be sent.
          example: "general"
        include_app_name_and_time: 
          type: boolean
          description: |
            Whether to include the application's name and timestamp within the banner image.
            
            **Supported Templates:** Image Banner with Text
        background_color_code: 
          type: string
          description: |
            The hex code for the notification's background color.
            
            **Supported Templates:** Stylized Basic, Simple Image Carousel, Image Banner with Text
          example: "#9a4444"
        app_name_color_code: 
          type: string
          description: |
            The hex code for the color of the application's name text.
            
            **Supported Templates:** Stylized Basic, Simple Image Carousel, Image Banner with Text
          example: "#dea1a1"
        notification_control_color: 
          type: string
          enum: [LIGHT, DARK]
          description: |
            The color scheme for the notification's control elements (action buttons).
            
            **Supported Templates:** Stylized Basic, Simple Image Carousel, Image Banner with Text
        include_title_and_message: 
          type: boolean
          description: |
            Whether to include the notification's title and message text within the banner image.
            
            **Supported Templates:** Image Banner with Text
        apply_background_color_in_text_editor: 
          type: boolean
          description: |
            Whether to apply the specified background color within the rich text editor for preview.
            
            **Supported Templates:** Stylized Basic, Simple Image Carousel, Image Banner with Text
        title: 
          type: string
          description: The main title of the push notification.
          example: "Limited Time Offer!"
        message: 
          type: string
          description: |
            The message body of the push notification.
            
            You can use HTML in the message parameter to apply rich text formatting, including text color and styles.
          example: "Get 50% off on all items. Shop now!"
        summary: 
          type: string
          description: The summary text for the notification.
        image_url: 
          type: string
          format: uri
          description: The image URL for the push notification.
          example: "https://example.com/images/promo.jpg"
        image_scaling: 
          type: string
          enum: [FIT_INSIDE_IMAGE_CONTAINER, FILL_IMAGE_CONTAINER]
          description: |
            The scaling behavior for images within the carousel template.
            
            **Supported Templates:** Simple Image Carousel, Image Banner with Text
        banner_image_url: 
          type: string
          format: uri
          description: |
            The URL for the background image used in the Image Banner Text template.
            
            **Required for:** Image Banner with Text template
        input_gif_url: 
          type: string
          format: uri
          description: |
            The URL for the GIF media used in the push campaign content.
            
            **Supported Templates:** Basic
        collapsed_push_notification: 
          type: string
          description: |
            The configuration for the notification's collapsed state (view before user expands it).
            
            **Supported Templates:** Image Banner with Text
          example: "SAME_AS_TEMPLATE_BACKUP"
        carousel_content: { $ref: '#/components/schemas/CarouselContent' }
        default_click_action: 
          type: string
          enum: [DEEPLINKING, NAVIGATE_TO_A_SCREEN, RICH_LANDING]
          description: The action performed when the main body of the notification is clicked.
        default_click_action_value: 
          type: string
          description: The URL or deep link to open when the notification is clicked.
          example: "https://example.com/sale"
        key_value_pairs: 
          type: array
          items: { $ref: '#/components/schemas/KeyValuePair' }
          description: Custom key-value pairs for the notification payload.

    CarouselContent:
      type: object
      description: |
        Configuration for image carousel in Simple Image Carousel template.
        
        **Required for:** Simple Image Carousel template
      properties:
        slider_transition:
          type: string
          enum: [MANUAL, AUTOMATIC]
          description: |
            The transition type for the carousel slides.

            In earlier versions of this API, the accepted values were `manual` and `automatic` (lowercase). They are now `MANUAL` and `AUTOMATIC` (uppercase). Update any existing integrations that pass lowercase values.
        slide_data:
          type: array
          description: Array of slide configurations.
          items:
            type: object
            properties:
              image_url: 
                type: string
                format: uri
                description: The image URL for this slide.
              image_click_action: 
                type: string
                enum: [DEEPLINKING, RICH_LANDING, NAVIGATE_TO_A_SCREEN]
                description: The click action for this slide's image.
              image_click_action_value: 
                type: string
                description: |
                  The click action value for this slide's image.
                  
                  **Required** when image_click_action is provided.
              key_value_pairs: 
                type: array
                items: { $ref: '#/components/schemas/KeyValuePair' }

    AndroidTimer:
      type: object
      description: |
        Timer configuration for Timer and Timer with Progress Bar templates.
        
        **Required for:** TIMER and TIMER_WITH_PROGRESS_BAR templates
      properties:
        timer_ends_at: 
          type: string
          enum: [DURATION, SPECIFIC_TIME_USER_TIMEZONE, SPECIFIC_TIME_CAMPAIGN_TIMEZONE]
          description: How the timer's endpoint is determined.
        specific_time: 
          type: string
          format: date-time
          description: |
            The specific time when the timer ends.
            
            **Required** when personalized_value is true.
        time_period: 
          type: string
          description: |
            The time period for the timer.
            
            **Required** when timer_ends_at is SPECIFIC_TIME_USER_TIMEZONE or SPECIFIC_TIME_CAMPAIGN_TIMEZONE.
        personalized_value: 
          type: boolean
          description: |
            Whether the timer duration is personalized per user.
            
            If false, all users get the same duration.
        duration_hour: 
          type: string
          description: |
            The number of hours the timer will run for.
            
            **Required** when personalized_value is false.
          example: "2"
        duration_minute: 
          type: string
          description: |
            The number of minutes the timer will run for (in addition to hours).
            
            **Required** when personalized_value is false.
          example: "30"

    AndroidButton:
      type: object
      description: Action button configuration for Android push notifications.
      properties:
        btn_name: 
          type: string
          description: The text to be displayed on the button.
          example: "Shop Now"
        click_action_type: 
          type: string
          enum: [DEEPLINKING, NAVIGATE_TO_A_SCREEN, RICH_LANDING, CALL, SHARE, COPY, SET_USER_ATTRIBUTE, TRACK_EVENT, CUSTOM_ACTION]
          description: The type of action to perform when the button is clicked.
        click_action_name: 
          type: string
          description: The name of the click action.
        click_action_value: 
          type: string
          description: The URL or deep link to open for the button's action.
          example: "https://example.com/product"
        key_value_pairs: 
          type: array
          items: { $ref: '#/components/schemas/KeyValuePair' }
          description: Custom key-value pairs specific to this button's click event.

    AndroidAdvanced:
      type: object
      description: Platform-level advanced settings for push delivery - TTL, priority, badge count, and similar controls.
      properties:
        coupon_code: 
          type: string
          description: The coupon code to be included in the push payload.
          example: "SUMMER50"
        icon_type_in_notification: 
          type: string
          description: The icon type to be included in the push payload.
          example: "app_icon"
        use_large_icon: 
          type: boolean
          description: Whether to use a large icon in the notification.
        make_notification_sticky: 
          type: boolean
          description: |
            When enabled, the user cannot swipe away the notification.
        dismiss_button_text: 
          type: string
          description: |
            The text to display on the dismiss button.
            
            **Required** when make_notification_sticky is true or auto_dismiss_notification is true.
        auto_dismiss_notification: 
          type: boolean
          description: Whether the notification can be auto-dismissed.
        auto_dismiss_notification_time_value: 
          type: integer
          description: |
            The time value after which to auto-dismiss the notification.
            
            **Required** when auto_dismiss_notification is true.
        auto_dismiss_notification_time_granularity: 
          type: string
          enum: [DAYS, HOURS, MINUTES]
          description: |
            The time unit for auto-dismiss.
            
            **Required** when auto_dismiss_notification is true.
        group_key: 
          type: string
          description: |
            The group key used to identify and categorize related push notifications.
            
            **Note:**
            - Use the same group key for all push notifications you want to group
            - MoEngage automatically modifies the group key to ensure it doesn't exceed 45 characters
            - Non-Latin scripts, special characters, and spaces are removed
        collapse_replace_key: 
          type: string
          description: |
            The update key used to identify and update related push notifications.
            
            Ensure you use the same update key for all push notifications intended to update each other.

    AndroidTemplateBackup:
      type: object
      description: |
        Fallback notification content for when the template cannot be rendered.
        
        **Required for:** Stylized Basic, Simple Image Carousel, Image Banner with Text, Timer, and Timer with Progress Bar templates
      properties:
        title: 
          type: string
          description: The title for the fallback notification.
        message: 
          type: string
          description: The message body for the fallback notification.
        summary: 
          type: string
          description: The summary for the fallback notification.
        image_url: 
          type: string
          format: uri
          description: The URL of an image for the fallback notification.
        default_click_action: 
          type: string
          enum: [DEEPLINKING, NAVIGATE_TO_A_SCREEN, RICH_LANDING]
          description: The default click action for the fallback notification.
        default_click_action_value: 
          type: string
          description: The URL or deep link for the fallback's click action.
        key_value_pairs: 
          type: array
          items: { $ref: '#/components/schemas/KeyValuePair' }
          description: Custom key-value pairs specific to the fallback payload.

    KeyValuePair:
      type: object
      description: A custom key-value pair attached to the push payload.
      properties:
        key: 
          type: string
          description: The key name.
        value: 
          type: string
          description: The value.

    IOSPushContent:
      type: object
      description: iOS push notification content.
      properties:
        template_type: 
          type: string
          enum: [BASIC, STYLIZED_BASIC, SIMPLE_IMAGE_CAROUSEL, Custom]
          description: |
            The type of iOS push template.

        custom_template_id: 
          type: string
          description: |
            The ID of the custom template.
            
            **Required** when template_type is "Custom".
        custom_template_version: 
          type: integer
          description: The version of the custom template.
        basic_details: { $ref: '#/components/schemas/IOSBasicDetails' }
        buttons: 
          type: array
          items: { $ref: '#/components/schemas/IOSButton' }
          description: Action buttons for the notification.
        advanced: { $ref: '#/components/schemas/IOSAdvanced' }
        template_backup: { $ref: '#/components/schemas/IOSTemplateBackup' }

    IOSBasicDetails:
      type: object
      description: Basic details for the iOS push notification.
      properties:
        background_color_code: 
          type: string
          description: |
            The hexadecimal color code for the notification's background.
            
            **Supported Templates:** Simple Image Carousel, Stylized Basic
          example: "#a0a0a0"
        apply_background_color_in_text_editor: 
          type: boolean
          description: |
            Whether to apply the background color within the text editor view.
            
            **Supported Templates:** Simple Image Carousel, Stylized Basic
        title: 
          type: string
          description: The main title of the push notification.
          example: "New Message"
        message: 
          type: string
          description: The main body text of the notification.
          example: "You have a new message waiting for you"
        subtitle: 
          type: string
          description: The subtitle displayed below the main title.
        allow_bg_refresh: 
          type: boolean
          description: Whether to allow the app to be woken up in the background to refresh content.
        rich_media_type: 
          type: string
          enum: [Image, Video, GIF]
          description: |
            The type of rich media to be included in the notification.
            
            **Supported Templates:** Basic
        rich_media_value: 
          type: string
          format: uri
          description: |
            The URL of the rich media asset specified in the rich_media_type field.
            
            **Supported Templates:** Basic
        image_url: 
          type: string
          format: uri
          description: |
            The URL of a large image to be displayed within the notification content.
            
            **Note:** Required when template_type is SIMPLE_IMAGE_CAROUSEL.
        input_gif_url: 
          type: string
          format: uri
          description: |
            The URL for the GIF media used in the push campaign content.
            
            **Supported Templates:** Basic, Stylized Basic
        carousel_content: { $ref: '#/components/schemas/IOSCarouselContent' }
        default_click_action: 
          type: string
          enum: [DEEPLINKING, NAVIGATE_TO_A_SCREEN, RICH_LANDING]
          description: The action performed when the main body of the notification is tapped.
        default_click_action_value: 
          type: string
          description: The URL or deep link associated with the default click action.
        key_value_pairs: 
          type: array
          items: { $ref: '#/components/schemas/KeyValuePair' }
          description: Custom key-value pairs sent with the push payload for in-app handling.

    IOSCarouselContent:
      type: object
      description: |
        Configuration for image carousel in Simple Image Carousel template.
        
        **Required for:** Simple Image Carousel template
      properties:
        slider_transition:
          type: string
          enum: [MANUAL, AUTOMATIC]
          description: |
            The transition type for the carousel slides.

            In earlier versions of this API, the accepted values were `manual` and `automatic` (lowercase). They are now `MANUAL` and `AUTOMATIC` (uppercase). Update any existing integrations that pass lowercase values.
        slide_data:
          type: array
          description: Array of slide configurations.
          items:
            type: object
            properties:
              image_url: 
                type: string
                format: uri
                description: The image URL for this slide.
              image_click_action: 
                type: string
                enum: [DEEPLINKING, RICH_LANDING, NAVIGATE_TO_A_SCREEN]
                description: The click action for this slide's image.
              image_click_action_value: 
                type: string
                description: The click action value for this slide's image.
              key_value_pairs: 
                type: array
                items: { $ref: '#/components/schemas/KeyValuePair' }

    IOSButton:
      type: object
      description: Action button configuration for iOS push notifications.
      properties:
        button_category: 
          type: string
          description: |
            The pre-defined category name for a set of interactive buttons configured in the app.
          example: "MOE_PUSH_TEMPLATE"

    IOSAdvanced:
      type: object
      description: Platform-level advanced settings for push delivery - TTL, priority, badge count, and similar controls.
      properties:
        coupon_code: 
          type: string
          description: The coupon code to be included in the push payload.
        sound_file: 
          type: string
          description: The name of a custom sound file located in the app bundle to play upon receiving the notification.
        enable_ios_badge: 
          type: boolean
          description: Whether this campaign allows the notification to increment the app's badge count.
        group_key: 
          type: string
          description: |
            The group key used to identify and categorize related push notifications.
            
            **Note:**
            - Use the same group key for all push notifications you want to group
            - MoEngage automatically modifies the group key to ensure it doesn't exceed 45 characters
            - Non-Latin scripts, special characters, and spaces are removed
        collapse_replace_key: 
          type: string
          description: |
            The update key used to identify and update related push notifications.
            
            Ensure you use the same update key for all push notifications intended to update each other.

    IOSTemplateBackup:
      type: object
      description: |
        Fallback notification content for when the template cannot be rendered.
        
        **Required for:** Stylized Basic and Simple Image Carousel templates
      properties:
        title: 
          type: string
          description: The title for the fallback notification.
        message: 
          type: string
          description: The message body for the fallback notification.
        subtitle: 
          type: string
          description: The subtitle for the fallback notification.
        allow_bg_refresh: 
          type: boolean
          description: Whether to enable background app refresh for the fallback notification.
        rich_media_type: 
          type: string
          enum: [Image, Video, GIF]
          description: The type of media attachment for the fallback.
        rich_media_value: 
          type: string
          format: uri
          description: The URL of the media attachment for the fallback.
        default_click_action: 
          type: string
          enum: [DEEPLINKING, NAVIGATE_TO_A_SCREEN, RICH_LANDING]
          description: The default click action for the fallback notification.
        default_click_action_value: 
          type: string
          description: The URL or deep link for the fallback's click action.
        key_value_pairs: 
          type: array
          items: { $ref: '#/components/schemas/KeyValuePair' }
          description: Custom key-value pairs specific to the fallback payload.

    WebPushContent:
      type: object
      description: Web push notification content.
      properties:
        template_type: 
          type: string
          enum: [BASIC]
          description: The template type for web push (currently only BASIC is supported).
        basic_details: { $ref: '#/components/schemas/WebBasicDetails' }
        buttons: 
          type: array
          items: { $ref: '#/components/schemas/WebButton' }
          description: |
            Action buttons for the notification.

        advanced: { $ref: '#/components/schemas/WebAdvanced' }

    WebBasicDetails:
      type: object
      description: Basic details for the Web push notification.
      properties:
        title: 
          type: string
          description: The title text displayed at the top of the notification.
          example: "Special Offer"
        message: 
          type: string
          description: The main body text of the notification.
          example: "Check out our latest deals!"
        redirect_url: 
          type: string
          format: uri
          description: The URL that the user is redirected to when they click the main body of the notification.
          example: "https://example.com/offers"
        image_url: 
          type: string
          format: uri
          description: The URL of a large image to be displayed within the notification content.
        auto_dismiss_notification: 
          type: boolean
          description: Whether the notification should auto-dismiss.

    WebButton:
      type: object
      description: Action button configuration for Web push notifications.
      properties:
        title: 
          type: string
          description: The text displayed on the button.
          example: "View Offer"
        icon_url: 
          type: string
          format: uri
          description: The URL of an icon to be displayed next to the button text.
        url: 
          type: string
          format: uri
          description: The destination URL that the user is redirected to when they click this button.

    WebAdvanced:
      type: object
      description: |
        Platform-level advanced settings for push delivery - TTL, priority, badge count, and similar controls.

      properties:
        icon_image_type: 
          type: string
          enum: [DEFAULT, ICON_URL]
          description: The type of icon to use for the notification.
        icon_url: 
          type: string
          format: uri
          description: The URL for a custom notification icon.

    EmailContent:
      type: object
      description: Email campaign content.
      properties:
        subject: 
          type: string
          description: The subject line of the email.
          example: "Exclusive Summer Sale - 50% Off!"
        preview_text: 
          type: string
          description: The preview text shown in email clients.
          example: "Don't miss out on our biggest sale of the season"
        sender_name: 
          type: string
          description: The name of the sender that appears in the email.
          example: "MoEngage Team"
        from_address: 
          type: string
          description: |
            The sender's email address.

            This field accepts personalization tokens, for example `{{UserAttribute['custom_from_email']}}`, resolved per-recipient at send time. **Requires the `EMAIL_FROM_ADDRESS_PERS` flag enabled for the workspace.**

            ```json
            { "from_address": "{{UserAttribute['custom_from_email']}}" }
            ```
          example: "noreply@example.com"
        reply_to_address: 
          type: string
          description: |
            The reply-to email address.

            This field accepts personalization tokens, for example `{{UserAttribute['custom_reply_to']}}`, resolved per-recipient at send time. Works unconditionally — no flag required.

            ```json
            { "reply_to_address": "{{UserAttribute['custom_reply_to']}}" }
            ```
          example: "support@example.com"
        cc_ids:
          type: array
          items:
            type: string
            format: email
          description: Email addresses to CC. For runnable examples, refer to [Email content variants](/api/campaigns/campaign-content-reference#email-content-variants).
        bcc_ids:
          type: array
          items:
            type: string
            format: email
          description: Email addresses to BCC. For runnable examples, refer to [Email content variants](/api/campaigns/campaign-content-reference#email-content-variants).
        html_content: 
          type: string
          description: |
            The HTML content of the email.
            
            **Optional** if custom_template_id is provided.
          example: "<!DOCTYPE html><html><head></head><body><p>Hello {{UserAttribute['First Name']}}</p></body></html>"
        email_editor: 
          type: string
          enum: ['Froala Editor', 'Ace Editor']
          description: |
            The HTML editor used for the email campaign.
            
            - **Required** if you want to create the campaign using the `Ace Editor`.
            - **Optional** if you want to use the default `Froala Editor`.

          example: "Ace Editor"
        custom_template_id: 
          type: string
          description: |
            The ID of a custom email template.
            
            **Optional** if html_content is provided.
            
            When this field is provided, the following fields are not required:
            - subject
            - preview_text
            - sender_name

        custom_template_version: 
          type: integer
          description: The version of the custom template.
        attachments:
          type: array
          items:
            type: object
            properties:
              file_type: 
                type: string
                enum: [URL, PERSONALIZED_ATTACHMENT]
              url: 
                type: string
              s3_keys:
                type: array
                items:
                  type: string
                description: |
                  S3 object keys backing an attachment stored in a fixed, MoEngage-owned S3 bucket. This is complementary to `url` / `file_type` on the same attachment entry, not a replacement — `url` and `file_type` are still required regardless of whether `s3_keys` is also provided.

                  No workspace-level configuration (such as bucket allowlisting) is required or supported, since the bucket is not customer-owned.
                example: ["attachments/2026/06/invoice-001.pdf"]
          description: |
            Attachments to include in the email.
        gmail_annotations:
          $ref: '#/components/schemas/GmailAnnotations'
        link_branding_domain:
          type: string
          description: |
            Custom domain for wrapping tracked links, in place of the default MoEngage tracking domain. Configured per workspace under **Settings** > **Link Branding**.

            - If supplied, must match a domain already configured for the workspace.
            - Optional — a campaign is accepted without this field even when the workspace has a matching domain configured. Omitting it falls back to the default MoEngage tracking domain.
            - Passing an empty string (`""`) is treated the same as omitting the field.
            - Supported for all delivery types including API_TRIGGERED.
          example: "yourbrand.com"
        deep_link_domain:
          type: string
          description: |
            Custom domain for deep/branch links in the email, in place of the default deep link behavior. Configured per workspace under **Settings** > **Deep Links**.

            - If supplied, must match a domain already configured for the workspace.
            - Optional — a campaign is accepted without this field even when the workspace has a matching domain configured. Omitting it uses the default deep link behavior.
            - Can be set independently of `link_branding_domain`.
            - Supported for all delivery types including API_TRIGGERED.
          example: "deeplink.yourbrand.com"


    # ==================== Gmail Annotations ====================
    GmailAnnotations:
      type: object
      description: |
        Gmail Annotations shown in Gmail's Promotions tab. Two annotation types are supported: `deal_card` and `product_carousel`. These are mutually exclusive — include only one per campaign. Passing both `deal_card` and `product_carousel` returns a 400 validation error.

        **Note:** `gmail_annotations` is only supported when `content_type` is `PROMOTIONAL`. Using it with `TRANSACTIONAL` returns a `400` error.
      required:
        - send_email_if_personalization_fails
        - sender_logo
        - sender_logo_type
      properties:
        send_email_if_personalization_fails:
          type: boolean
          description: |
            Whether to send the email if personalization of any Gmail annotation field fails.

            **Required** when `gmail_annotations` is present.
          example: true
        sender_logo:
          type: string
          description: |
            The sender logo shown in the Gmail annotation.

            Accepts a valid HTTPS URL or a MoEngage personalization token.

            **Required** when `gmail_annotations` is present.
          example: "https://example.com/logo.png"
        sender_logo_type:
          type: string
          enum:
            - image_url
            - uploaded_image
          description: |
            The source type of the sender logo.

            - `image_url`: `sender_logo` is treated as a publicly accessible image URL.
            - `uploaded_image`: `sender_logo` is treated as an uploaded image reference from the MoEngage asset manager.

            **Required** when `sender_logo` is provided.
          example: "image_url"
        deal_card:
          description: |
            The Deal Card annotation.

            **Optional.** Use either `deal_card` or `product_carousel` — not both. Passing both `deal_card` and `product_carousel` returns a 400 validation error.
          allOf:
            - $ref: '#/components/schemas/GmailAnnotationsDealCard'
        product_carousel:
          description: |
            The Product Carousel annotation.

            **Optional.** Use either `product_carousel` or `deal_card` — not both. Passing both `deal_card` and `product_carousel` returns a 400 validation error.
          allOf:
            - $ref: '#/components/schemas/GmailAnnotationsProductCarousel'

    GmailAnnotationsDealCard:
      type: object
      description: |
        Deal Card annotation shown in Gmail's Promotions tab. All four fields below are required when `deal_card` is present.
      required:
        - description
        - discount_code
        - availability_starts
        - availability_ends
      properties:
        description:
          type: string
          description: |
            Short deal description shown in the Gmail annotation.

            **Required** when `deal_card` is present.
          example: "Get 20% off on all orders above $50"
        discount_code:
          type: string
          description: |
            Promo or discount code displayed with the deal.

            **Required** when `deal_card` is present.
          example: "SAVE20"
        availability_starts:
          type: string
          description: |
            ISO 8601 datetime indicating when the deal becomes active. Use the timezone of the promotion, not your server or API caller timezone. Ensure the timezone matches the promotion's local timezone so the deal displays the correct active and expiry times for your audience.

            **Accepted formats:**
            - UTC (`Z` suffix): `YYYY-MM-DDTHH:mm:ssZ` (for example, `2026-06-01T00:00:00Z`).
            - Positive offset: `YYYY-MM-DDTHH:mm:ss+HH:MM` (for example, `2026-06-01T00:00:00+05:30` for IST).
            - Negative offset: `YYYY-MM-DDTHH:mm:ss-HH:MM` (for example, `2026-06-01T00:00:00-07:00` for US Pacific PDT).

            **Common offsets:** IST `+05:30`, SGT `+08:00`, GST `+04:00`, CET `+01:00` or `+02:00` (DST), EST `-05:00`, EDT `-04:00`, PST `-08:00`, PDT `-07:00`, UTC `Z` or `+00:00`.

            **Required** when `deal_card` is present.
          example: "2026-06-01T00:00:00Z"
        availability_ends:
          type: string
          description: |
            ISO 8601 datetime indicating when the deal expires. Same format as `availability_starts`.

            Use the timezone of the promotion, not your server or API caller timezone. Ensure the timezone matches the promotion's local timezone so the deal displays the correct active and expiry times for your audience.

            Must be at least 1 hour after `availability_starts`.

            **Required** when `deal_card` is present.
          example: "2026-06-30T23:59:59Z"

    GmailAnnotationsProductCarousel:
      type: object
      description: |
        Product Carousel annotation shown in Gmail's Promotions tab.

        Use `MANUAL` to specify products directly in the request, or `PRODUCT_SET` to pull products dynamically from a MoEngage Catalog product set.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - MANUAL
            - PRODUCT_SET
          description: |
            The source type for the product carousel.

            - `MANUAL`: Specify products directly in the request using `manual_data`.
            - `PRODUCT_SET`: Pull products dynamically from a MoEngage Catalog product set using `product_set_data`. Requires the Product Sets feature to be enabled for your workspace.

            **Required** when `product_carousel` is present.
        manual_data:
          description: |
            Products specified directly in the request.

            **Required** when `product_carousel.type` is `MANUAL`.
          allOf:
            - $ref: '#/components/schemas/GmailAnnotationsProductCarouselManualData'
        product_set_data:
          description: |
            Reference to a MoEngage Catalog product set.

            **Required** when `product_carousel.type` is `PRODUCT_SET`.
          allOf:
            - $ref: '#/components/schemas/GmailAnnotationsProductCarouselProductSetData'

    GmailAnnotationsProductCarouselManualData:
      type: object
      description: |
        Manual product list for the Gmail Annotations product carousel.

        **Required** when `product_carousel.type` is `MANUAL`. Minimum 2 products required.
      required:
        - currency
        - products
      properties:
        currency:
          type: string
          description: |
            ISO 4217 currency code applied to all products in the carousel.

            **Required** when `manual_data` is present.
          example: "USD"
        products:
          type: array
          description: |
            List of products to display in the carousel.

            **Required** when `manual_data` is present. Minimum 2 products.
          minItems: 2
          items:
            $ref: '#/components/schemas/GmailAnnotationsProductCarouselManualProduct'

    GmailAnnotationsProductCarouselManualProduct:
      type: object
      description: A single product entry in a manual product carousel. All fields below are required for each product.
      required:
        - id
        - headline
        - original_price
        - discount_value
        - discount_type
        - promo_url
        - product_image
      properties:
        id:
          type: string
          description: |
            Unique product identifier.

            **Required** for each product.
          example: "prod-001"
        headline:
          type: string
          description: |
            Product name shown in the annotation.

            **Required** for each product.
          example: "Wireless Headphones"
        original_price:
          type: string
          description: |
            Original price of the product as a numeric string.

            **Required** for each product.
          example: "199.99"
        discount_value:
          type: string
          description: |
            Discount amount as a numeric string. Interpreted as a percentage or absolute value depending on `discount_type`.

            **Required** for each product.
          example: "30"
        discount_type:
          type: string
          enum:
            - PERCENT
            - VALUE
          description: |
            How `discount_value` is interpreted.

            - `PERCENT`: `discount_value` is a percentage.
            - `VALUE`: `discount_value` is an absolute amount in `currency`.

            **Required** for each product.
          example: "PERCENT"
        promo_url:
          type: string
          description: |
            Product landing page URL. Must be a valid HTTPS URL.

            **Required** for each product.
          example: "https://example.com/products/prod-001"
        product_image:
          type: string
          description: |
            Product image URL. Must be a valid HTTPS URL. Cannot be empty.

            **Required** for each product.
          example: "https://example.com/images/prod-001.png"

    GmailAnnotationsProductCarouselProductSetData:
      type: object
      description: |
        Product set configuration for the Gmail Annotations product carousel. Pulls products dynamically from a MoEngage Catalog product set.

        **Required** when `product_carousel.type` is `PRODUCT_SET`. Requires the Product Sets feature to be enabled for your workspace.
      required:
        - product_set
        - image_url
        - headline
        - promo_url
        - product_count
      properties:
        product_set:
          type: string
          description: |
            Product set ID from MoEngage Catalog. Must be valid and exist in your workspace.

            **Required** when `product_set_data` is present.
          example: "product_set_12345"
        image_url:
          type: string
          description: |
            Fallback image URL if a product set image is unavailable.

            **Required** when `product_set_data` is present.
          example: "https://example.com/images/fallback.png"
        headline:
          type: string
          description: |
            Label shown for the product carousel.

            **Required** when `product_set_data` is present.
          example: "Recommended For You"
        promo_url:
          type: string
          description: |
            Destination URL when the user clicks the annotation.

            **Required** when `product_set_data` is present.
          example: "https://example.com/shop"
        product_count:
          type: integer
          description: |
            Number of products to display.

            **Required** when `product_set_data` is present. Minimum 2, maximum 9.
          minimum: 2
          maximum: 9
          example: 3


    SegmentationDetails:
      type: object
      description: |
        Defines the target audience for the campaign.

        For included/excluded filter combinations, filter primitives (`user_attributes`, `actions`, `custom_segments`), and opt-out targeting, refer to [Campaign audience](/api/campaigns/audience-scheduling-delivery-reference#campaign-audience).


      properties:
        included_filters: { $ref: '#/components/schemas/FilterGroup' }
        excluded_filters:
          allOf:
            - $ref: '#/components/schemas/FilterGroup'
          description: |
            Filters that exclude users from the campaign audience.

        is_all_user_campaign: 
          type: boolean
          description: Whether to include all users in the campaign.
        send_campaign_to_opt_out_users:
          type: boolean
          description: Whether to send the campaign to users who have opted out. For runnable examples, refer to [Campaign audience](/api/campaigns/audience-scheduling-delivery-reference#campaign-audience).

    FilterGroup:
      type: object
      description: |
        A group of filters combined with a logical operator.
        
        For detailed segmentation payload and supported fields, refer to [Create Custom Segment](/api/filter-segments/create-filter-segment).
      properties:
        filter_operator: 
          type: string
          enum: [and, or]
          description: The logical operator to combine filters.
        filters:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/UserAttributeFilter'
              - $ref: '#/components/schemas/ActionFilter'
              - $ref: '#/components/schemas/CustomSegmentFilter'
          description: |
            The list of filters to be combined using the filter operator.
            
            Supported filter types:
            - User attributes-based filters
            - Action-based filters (with or without attributes)
            - Custom segments

    UserAttributeFilter:
      type: object
      title: "User attributes-based filters"
      description: Filter based on user attributes.
      properties:
        filter_type: 
          type: string
          enum: [user_attributes]
        data_type: 
          type: string
          enum: [string, double, datetime, bool]
          description: The data type of the attribute being filtered.
        category: 
          type: string
          description: The category of the attribute (e.g., "Tracked Standard Attribute").
        name: 
          type: string
          description: The name of the attribute to filter on (e.g., "uid").
        operator: 
          type: string
          description: |
            The operator to use in the filter. Allowed values depend on data_type:
            - bool: is, exists
            - double: in, between, lessThan, greaterThan, exists
            - string: in, contains, containsInTheFollowing, startWithInTheFollowing, endsWithInTheFollowing, exists, is
            - datetime: inTheLast, on, between, before, after, inTheNext, exists, today
        value: 
          description: The value to filter on (not required for 'exists' operator).
        case_sensitive: 
          type: boolean
          description: Whether the filter comparison should be case-sensitive.
        negate: 
          type: boolean
          description: Whether to negate the filter condition.
        is_dynamic_value:
          type: boolean
          description: |
            When `true`, the filter value is treated as a dynamic expression and resolved at send time rather than evaluated as a literal string.

            Set this to `true` for Business Event-triggered campaigns where the filter value references a Business Event attribute, for example, `{{BusinessEventAttribute['season']}}`.
        project_name:
          type: string
          description: |
            The name of the project associated with the user attributes.

            **Required** if the Portfolio feature is enabled in your workspace.

    ActionFilter:
      type: object
      title: "Action-based filters (with or without attributes)"
      description: |
        Filter based on user actions/events. Use inside `segmentation_details.included_filters.filters` or `trigger_condition.included_filters.filters`.

      properties:
        filter_type: 
          type: string
          enum: [actions]
        action_name: 
          type: string
          description: The name of the action/event to filter on.
        execution:
          type: object
          properties:
            type: 
              type: string
              enum: [atleast, atmost, exactly]
            count: 
              type: integer
        executed: 
          type: boolean
          description: Whether the action was executed.
        attributes: { $ref: '#/components/schemas/FilterGroup' }
        condition: 
          type: string
          description: The condition type. Must be passed as the string "IF".

    CustomSegmentFilter:
      type: object
      title: "Custom segments"
      description: Filter using a custom segment.
      properties:
        filter_type: 
          type: string
          enum: [custom_segments]
        name: 
          type: string
          description: The name of the custom segment.
        id: 
          type: string
          description: The ID of the custom segment.

    SchedulingDetails:
      type: object
      description: |
        Defines when the campaign should be sent.

        For per-delivery-type runnable payloads (`ASAP`, `AT_FIXED_TIME`, `SEND_IN_BTS`, `SEND_IN_USER_TIMEZONE`, and `PERIODIC` with `periodic_details`), refer to [Campaign delivery schedule](/api/campaigns/audience-scheduling-delivery-reference#campaign-delivery-schedule).
      properties:
        delivery_type: 
          type: string
          enum: [ASAP, AT_FIXED_TIME, SEND_IN_BTS, SEND_IN_USER_TIMEZONE]
          description: |
            When to deliver the campaign.

        start_time:
          type: string
          format: date-time
          description: |
            The start time for the campaign in ISO 8601 format. Interpreted in the timezone specified by the `timezone` field. If `timezone` is not provided, pass this value in UTC.

            Example: "2024-06-21T12:59:00"
        expiry_time:
          type: string
          format: date-time
          description: |
            The expiry time for the campaign in ISO 8601 format. Interpreted in the timezone specified by the `timezone` field. If `timezone` is not provided, pass this value in UTC.

        timezone:
          type: string
          description: |
            IANA timezone string for the campaign schedule (for example, `Asia/Kolkata`). Required when `delivery_type` is `AT_FIXED_TIME` or `SEND_IN_BTS`. Optional for `SEND_IN_USER_TIMEZONE`, where each user's own timezone is used.
          example: "Asia/Kolkata"
        periodic_details: { $ref: '#/components/schemas/PeriodicDetails' }
        bts_details: { $ref: '#/components/schemas/BTSDetails' }
        user_timezone_details: { $ref: '#/components/schemas/UserTimezoneDetails' }
        geo_fence_timelimit:
          type: object
          description: |
            Defines the delivery time window for location-triggered campaigns. When configured, notifications are delivered only within the specified schedule.

            **Applicable for:** `LOCATION_TRIGGERED` campaigns.
          properties:
            notification_schedule:
              type: string
              enum: [ALWAYS, LIMITED_TIME]
              description: Controls when delivery is permitted. Set to `ALWAYS` to allow delivery at any time, or `LIMITED_TIME` to restrict delivery to the configured time bounds.
            timebounds:
              type: array
              description: |
                One or more start and end time windows within which delivery is permitted.

                **Required** when `notification_schedule` is `LIMITED_TIME`.
              items:
                type: object
                properties:
                  start_time:
                    type: string
                    format: date-time
                    description: Start of the delivery window in ISO 8601 format.
                    example: "2027-06-19T11:02:00"
                  end_time:
                    type: string
                    format: date-time
                    description: End of the delivery window in ISO 8601 format.
                    example: "2029-06-20T13:55:00"

    PeriodicDetails:
      type: object
      description: |
        Configuration for periodic campaigns.

        **Required** for PERIODIC campaigns.

        For runnable Daily, Weekly, Monthly (specific dates), and Monthly (first Monday) examples, refer to [Periodic schedules](/api/campaigns/audience-scheduling-delivery-reference#periodic-schedules).
      properties:
        sending_frequency: 
          type: string
          enum: [DAILY, WEEKLY, MONTHLY]
          description: The frequency to send the campaign.
        repeat_frequency: 
          type: integer
          description: The repeat frequency of the campaign.
        no_of_occurences: 
          type: integer
          description: The number of occurrences of the campaign.
        repeat_on_date_of_month: 
          type: array
          items: { type: integer }
          description: |
            The dates of the month on which the campaign should be repeated.
            
            Example: [5, 25] to send on the 5th and 25th of each month.
        repeat_on_days_of_week: 
          type: array
          items: 
            type: string
            enum: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
          description: |
            The days of the week on which the campaign should repeat.

        repeat_on_days_of_week_for_month:
          type: array
          items:
            type: object
            properties:
              week_granularity: 
                type: string
                enum: [FIRST, SECOND, THIRD, FOURTH, LAST]
              repeat_on_days_of_week: 
                type: array
                items: 
                  type: string
                  enum: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
          description: |
            Configuration for repeating on specific weeks of the month.


    BTSDetails:
      type: object
      description: |
        Best Time to Send (BTS) configuration.

        BTS selects an optimal send time per user based on historical engagement patterns.

        For the field schema and required-when-`SEND_IN_BTS` rule, refer to [Best Time to Send](/api/campaigns/audience-scheduling-delivery-reference#best-time-to-send).
      properties:
        send_in_bts: 
          type: boolean
          description: Whether to send the campaign at the best time.
        if_user_bts_is_not_available: 
          type: string
          description: When to send the campaign if the user's best time is not available.
        if_user_bts_outside_time_window: 
          type: string
          description: When to send the campaign if the user's best time is outside the time window.
        window_end_time: 
          type: string
          description: The window end time.
          example: "6:43 am"

    UserTimezoneDetails:
      type: object
      description: |
        Configuration for sending in the user's timezone.

        For the field schema and required-when-`SEND_IN_USER_TIMEZONE` rule, refer to [User timezone](/api/campaigns/audience-scheduling-delivery-reference#user-timezone).
      properties:
        send_in_user_timezone: 
          type: boolean
          description: Whether to send the campaign on a specific date and time within the user's timezone.
        send_if_user_timezone_has_passed: 
          type: boolean
          description: Whether to send the campaign if the user's timezone has passed.

    PushDeliveryControls:
      type: object
      description: |
        Controls for Push campaign delivery behavior.

        For per-delivery-type runnable examples (throttle, event-triggered, device-triggered, location-triggered, queuing), refer to [Push delivery controls](/api/campaigns/audience-scheduling-delivery-reference#push-delivery-controls).
      properties:
        bypass_dnd: 
          type: boolean
          description: |
            Whether to bypass Do Not Disturb settings.
            
            Required for event-triggered campaigns.
        campaign_throttle_rpm:
          type: integer
          description: |
            The campaign throttle in requests per minute.

            Not applicable for device-triggered, location-triggered, and event-triggered campaigns.
          example: 50000
        count_for_frequency_capping: 
          type: boolean
          description: Whether to count this campaign for frequency capping.
        ignore_frequency_capping: 
          type: boolean
          description: Whether to ignore frequency capping for this campaign.
        minimum_delay_between_two_notification_in_hour: 
          type: integer
          description: |
            Minimum delay between two notifications in hours.
            
            Applies to event-triggered and device-triggered campaigns.
        max_time_to_show_message_of_same_camapign: 
          type: string
          description: |
            Maximum duration (in hours) that a message from this campaign will be displayed to a user.
            
            Applicable for device-triggered campaigns.
        expiry_time_of_sync_data_in_hour: 
          type: string
          description: |
            Duration (in hours) after which synced campaign data will expire if trigger condition is not met.
            
            Applicable for device-triggered campaigns.
        send_message_in_offline_mode: 
          type: boolean
          description: |
            Whether to store and deliver the message when the device is offline.
            
            Applicable for device-triggered campaigns.
        send_limit_value: 
          type: string
          description: |
            Maximum number of times a user can receive this campaign within the specified time granularity.
            
            Applicable for location-triggered campaigns.
        send_limit_granularity_in_hours: 
          type: string
          description: |
            Time window (in hours) during which the send_limit_value is enforced.
            
            Applicable for location-triggered campaigns.
        ignore_global_minimum_delay:
          type: boolean
          description: |
            Whether to bypass the global minimum delay setting configured at the workspace level.

            When `true`, this campaign ignores the workspace-wide minimum interval between push notifications
            and can be delivered to a user regardless of how recently they received another push. Use this
            for time-sensitive campaigns (for example, transactional or alert-style messages) where
            respecting the global delay would reduce delivery timeliness.

            Applies to event-triggered campaigns.
        queuing_enabled:
          type: boolean
          description: |
            Enables message queuing for this campaign. When set to `true`, messages that are temporarily blocked by DND, frequency capping, or minimum delay restrictions are held in a queue and delivered as soon as the restriction clears, rather than being dropped.

            **Supported delivery types:** `ONE_TIME`, `PERIODIC`, `EVENT_TRIGGERED`, `BUSINESS_EVENT_TRIGGERED`. Not applicable to `DEVICE_TRIGGERED` or `LOCATION_TRIGGERED` campaigns.

            **DND interaction:**
            - When `bypass_dnd` is `false` (DND respected): messages blocked during a DND window are queued and delivered once the window passes.
            - When `bypass_dnd` is `true` (DND ignored): queuing applies to frequency capping and minimum delay blocks only.

            **Auto-disabled:** When both `ignore_frequency_capping` and `minimum_delay_between_two_notification_in_hour` are configured to bypass all delivery restrictions, queuing is automatically disabled.

            **Queue limits:** Up to 10,000,000 messages for `ONE_TIME`, `PERIODIC`, and `BUSINESS_EVENT_TRIGGERED` campaigns; up to 1,000,000 for `EVENT_TRIGGERED` campaigns.

            **Delivery order:** Queued messages are delivered in first-in, first-out (FIFO) order.

        queue_duration:
          type: integer
          minimum: 0
          maximum: 48
          description: |
            The duration in hours during which a queued message will be retried for delivery. Accepted range: `1`–`48` hours. Set to `0` when `queuing_enabled` is `false`.

            If a user does not become eligible for delivery within the configured window, the message is dropped and the outcome is recorded in campaign analytics.

            For active `PERIODIC` and triggered campaigns, changes to this value apply only to messages queued after the update. Messages already in the queue retain the original duration.
        limit_send_config:
          type: object
          description: |
            Configuration for limiting how many times a user can receive this campaign within a specified rolling time window. When enabled, MoEngage drops deliveries to users who have already reached `max_count` within the window and records the outcome in campaign analytics.
            Applies to `PERIODIC` and `EVENT_TRIGGERED` campaigns. Not applicable to other delivery types.
          properties:
            enabled:
              type: boolean
              description: |
                Whether the send limit is enforced for this campaign. When `false`, `max_count`, `window_value`, and `window_unit` are ignored.
              example: true
            max_count:
              type: integer
              minimum: 1
              description: |
                The maximum number of times a user can receive this campaign within the window defined by `window_value` and `window_unit`.
              example: 1
            window_value:
              type: integer
              minimum: 1
              description: |
                The size of the rolling time window during which `max_count` is enforced. Combined with `window_unit` — for example, `window_value: 1` with `window_unit: DAYS` enforces the limit across a 24-hour rolling window. The maximum frequency window you can configure is **1 year**, which means, up to **12 months**, **52 weeks**, or **365 days**, depending on the unit you choose.
              example: 1
            window_unit:
              type: string
              enum:
                - HOURS
                - DAYS
                - WEEKS
                - MONTHS
                - YEAR
              description: |
                The unit of time for `window_value`. `HOURS` is applicable only to event triggered campaigns.
              example: DAYS
          required:
            - enabled
          
    EmailDeliveryControls:
      type: object
      description: |
        Controls for Email campaign delivery behavior.

        For runnable examples, refer to [Email delivery controls](/api/campaigns/audience-scheduling-delivery-reference#email-delivery-controls).
      properties:
        bypass_dnd: 
          type: boolean
          description: Whether to bypass Do Not Disturb settings.
        campaign_throttle_rpm: 
          type: integer
          description: The campaign throttle in requests per minute.
          example: 2000
        count_for_frequency_capping: 
          type: boolean
          description: Whether to count this campaign for frequency capping.
        ignore_frequency_capping: 
          type: boolean
          description: Whether to ignore frequency capping for this campaign.
        minimum_delay_between_two_notification_in_hour: 
          type: integer
          description: Minimum delay between two notifications in hours.
        limit_send_config:
          type: object
          description: |
            Configuration for limiting how many times a user can receive this campaign within a specified rolling time window. When enabled, MoEngage drops deliveries to users who have already reached `max_count` within the window and records the outcome in campaign analytics.

            Applies to `PERIODIC` and `EVENT_TRIGGERED` campaigns. Not applicable to transactional campaigns, which do not have delivery controls.
          properties:
            enabled:
              type: boolean
              description: |
                Whether the send limit is enforced for this campaign. When `false`, `max_count`, `window_value`, and `window_unit` are ignored.
              example: true
            max_count:
              type: integer
              minimum: 1
              description: |
                The maximum number of times a user can receive this campaign within the window defined by `window_value` and `window_unit`.
              example: 1
            window_value:
              type: integer
              minimum: 1
              description: |
                The size of the rolling time window during which `max_count` is enforced. Combined with `window_unit` — for example, `window_value: 1` with `window_unit: DAYS` enforces the limit across a 24-hour rolling window. The maximum frequency window you can configure is **1 year**, which means, up to **12 months**, **52 weeks**, or **365 days**, depending on the unit you choose.
              example: 1
            window_unit:
              type: string
              enum:
                - HOURS
                - DAYS
                - WEEKS
                - MONTHS
                - YEAR
              description: |
                The unit of time for `window_value`. `HOURS` is applicable only to event triggered campaigns.
              example: DAYS
          required:
            - enabled

    AdvancedDetails:
      type: object
      description: |
        Advanced Push delivery settings, including notification expiration and per-platform priority.

        For runnable iOS APNS priority and Android priority examples, refer to [Advanced Push settings](/api/campaigns/audience-scheduling-delivery-reference#advanced-push-settings).
      properties:
        expiration_settings:
          type: object
          properties:
            expire_notification_after_value: 
              type: integer
              description: The numeric value for the notification expiration time.
            expire_notification_after_type: 
              type: string
              enum: [HOUR, DAY]
              description: The time unit for notification expiration.
            remove_from_inbox_after_value: 
              type: integer
              description: The numeric value for when to remove the message from the inbox.
            remove_from_inbox_after_type: 
              type: string
              enum: [DAY]
              description: The time unit for removing the message from the inbox.
        platform_level_priority:
          type: object
          properties:
            android_specific_priority:
              type: object
              properties:
                send_with_priority: 
                  type: boolean
                  description: Whether to send with priority.
            ios_specific_priority:
              type: object
              properties:
                apns_priority: 
                  type: string
                  enum: ["1", "5", "10"]
                  description: The priority of notification delivery for APNS.
                interruption_level: 
                  type: string
                  enum: [PASSIVE, ACTIVE, TIME_SENSITIVE, CRITICAL]
                  description: The interruption level for iOS notifications.
                relevance_score: 
                  type: number
                  enum: [0, 0.5, 1]
                  description: The relevance score for iOS notifications.

    ConversionGoalDetails:
      type: object
      description: |
        Configuration for tracking campaign conversion goals.

        For runnable single-goal and multi-goal examples, refer to [Conversion goal tracking](/api/campaigns/audience-scheduling-delivery-reference#conversion-goal-tracking).
      properties:
        attribution_window_in_hours: 
          type: integer
          description: The attribution window in hours.
          example: 36
        goals: 
          type: array
          items: { $ref: '#/components/schemas/Goal' }
          description: List of conversion goals to track.

    Goal:
      type: object
      description: A single conversion goal configuration.
      properties:
        goal_name: 
          type: string
          description: The name of the goal.
          example: "Goal 1"
        goal_event_name: 
          type: string
          description: The event name associated with this goal.
        goal_event_attribute: { $ref: '#/components/schemas/GoalEventAttribute' }
        is_primary_goal: 
          type: boolean
          description: Whether this is the primary goal.
        revenue_attribute: 
          type: string
          description: The revenue attribute to track.
        revenue_currency: 
          type: string
          description: The currency for revenue tracking.

    GoalEventAttribute:
      type: object
      description: Attributes associated with the conversion goal event.
      properties:
        name: 
          type: string
          description: The name of the goal event attribute.
        condition: 
          type: string
          description: The condition used while creating the goal (e.g., "is", "contains", "between").
        data_type: 
          type: string
          enum: [STRING, DOUBLE, BOOL, NUMBER, GEOPOINT, DATETIME, ARRAY_DOUBLE, ARRAY_STRING, OBJECT, ARRAY_OBJECT]
          description: The data type of the attribute.
        value: 
          type: string
          description: |
            The value of the goal event attribute.
            Supported data types: STRING, DOUBLE, BOOL, NUMBER, GEOPOINT, DATETIME, ARRAY_DOUBLE, ARRAY_STRING.
        value1: 
          type: string
          description: |
            A secondary value, used for conditions like 'between'.
            Supported data types: STRING, DOUBLE, BOOL, NUMBER, GEOPOINT, DATETIME, ARRAY_DOUBLE, ARRAY_STRING.
        negate: 
          type: boolean
          description: Whether to negate the filter condition.
        value_type: 
          type: string
          description: The type of value being filtered.
        array_filter_type: 
          type: string
          description: The logical filter type for array attributes.
        filters: 
          type: array
          items: { type: object }
          description: A list of sub-filters used when data_type is OBJECT or ARRAY_OBJECT.
        is_case_sensitive: 
          type: boolean
          description: Whether the goal event attribute is case-sensitive.

    ControlGroupDetails:
      type: object
      description: |
        Configuration for control groups.

        For runnable campaign-control-group and global-control-group examples, refer to [Control groups](/api/campaigns/audience-scheduling-delivery-reference#control-groups).
      properties:
        is_campaign_control_group_enabled: 
          type: boolean
          description: Whether the campaign control group is enabled.
        campaign_control_group_percentage: 
          type: integer
          minimum: 0
          maximum: 100
          description: |
            The percentage of users added to the exclusion list.
            
            **Required** if is_campaign_control_group_enabled is true.
        is_global_control_group_enabled: 
          type: boolean
          description: |
            Whether the global control group is enabled.


    UTMParams:
      type: object
      description: |
        UTM parameters for tracking campaign performance. The five standard keys (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`) are explicitly defined.

        **Custom UTM parameters:** Up to 5 additional custom parameters can be passed as separate keys directly inside the `utm_params` object. Custom keys accept arbitrary names — the `utm_` prefix is a convention, not a requirement (for example, `utm_cust` or `campaign_source` are both accepted).

        For runnable examples, refer to [UTM parameters](/api/campaigns/audience-scheduling-delivery-reference#utm-parameters).
      properties:
        utm_source:
          type: string
          description: |
            The source of the traffic (for example, YouTube, Instagram, Google).

            **Required** when using UTM parameters.
          example: "{{utm_source}}"
        utm_medium:
          type: string
          description: |
            The channel type (for example, Push, SMS, Email).

            **Required** when using UTM parameters.
          example: "{{utm_medium}}"
        utm_campaign:
          type: string
          description: The name of the campaign (for example, Newyear, Bigbillionday).
          example: "{{utm_campaign}}"
        utm_term:
          type: string
          description: Search terms for paid traffic (for example, Mobile+sale).
        utm_content:
          type: string
          description: The content element that differentiates links (for example, banner, video).
        utm_custom:
          type: string
          description: |
            A single custom UTM parameter value. For multiple custom parameters, pass them as
            separate top-level keys inside the `utm_params` object using arbitrary `utm_`-prefixed
            names (for example, `utm_cust`, `utm_c1ust`, `utm_c2ust`). A maximum of 5 custom
            parameters is supported in total.
      additionalProperties:
        type: string
        description: |
          Arbitrary custom UTM parameters with `utm_`-prefixed key names (for example, `utm_cust`,
          `utm_c1ust`). Up to 5 custom parameters are supported in total across all custom keys.

    Connector:
      type: object
      description: |
        Email connector configuration for sending email campaigns. Required for inline Email test requests and before an Email campaign can be published or sent for testing. Not required at draft create time — it can be added later via `PATCH`. For runnable examples, refer to [Email delivery connector](/api/campaigns/campaign-content-reference#email-delivery-connector).
      required: [connector_type, connector_name]         
      properties:
        connector_type:
          type: string
          enum: [SENDGRID, AMAZON_SES, SPARKPOST, MANDRILL, CUSTOM_SMTP, CUSTOM_API, NETCORE]
          description: |
            The email service provider for this campaign. Must match a connector configured in your MoEngage workspace. Accepted values: `SENDGRID`, `AMAZON_SES`, `SPARKPOST`, `MANDRILL`, `CUSTOM_SMTP`, `CUSTOM_API`, `NETCORE`. To find which connectors are active, go to **Settings** > **Email** > **Connectors** in the MoEngage dashboard.
        connector_name: 
          type: string
          description: The name of the connector configuration as configured in your MoEngage workspace.

    CampaignAudienceLimit:
      type: object
      description: |
        Configuration for capping the number of users a campaign can reach (max-send).

        Campaign Audience Limit (also called max-send) caps how many users a single campaign can reach. Use it to control reach on high-volume campaigns and protect users from over-messaging. The cap can apply across the campaign's full lifetime (`frequency: TOTAL`) or per send instance (`frequency: INSTANCE`).

        For runnable `TOTAL` (lifetime cap), `INSTANCE` (per-send cap, Periodic Push only), and disabled-cap examples, refer to [Campaign audience cap](/api/campaigns/audience-scheduling-delivery-reference#campaign-audience-cap).

        **Supported channels:** Email, Push.

        **Supported delivery types:**
        - All delivery types support `frequency: TOTAL` (lifetime cap).
        - `frequency: INSTANCE` (per-send cap) is supported only for **Periodic Push** campaigns.
        - `campaign_audience_limit` is not supported for `BROADCAST_LIVE_ACTIVITY`.

        **Flag-gated feature:** This feature is not enabled by default for any workspace and requires
        explicit activation by your MoEngage account team. If you include `campaign_audience_limit` in
        a request on a workspace where the flag has not been enabled, the API returns a `400` with the
        following error body:

        ```json
        {
          "error": {
            "code": "VALIDATION_FAILED",
            "message": "Campaign Audience Limit feature is not enabled for this db",
            "target": "campaign_audience_limit",
            "details": [
              {
                "target": "campaign_audience_limit",
                "message": "Campaign Audience Limit feature is not enabled for this db"
              }
            ]
          },
          "response_id": "{{response_id}}"
        }
        ```

        When `is_campaign_audience_limit_enabled` is `true`, the fields `metric`, `frequency`, and
        `limit` are all required. When set to `false`, those three fields must not be provided.
        
        For `ONE_TIME` campaigns, only `limit` and `is_campaign_audience_limit_enabled` are supported. Do not pass `metric` or `frequency`, they are only valid for `PERIODIC` and `EVENT_TRIGGERED` campaigns. 
        Passing them for a ONE_TIME campaign causes the validate API to fail. 
        

      properties:
        is_campaign_audience_limit_enabled:
          type: boolean
          description: |
            Whether the campaign audience limit is active. Set to `true` to enforce the cap; `false` to disable.

            When `true`, `metric`, `frequency`, and `limit` are all required.
            When `false`, `metric`, `frequency`, and `limit` must not be provided.
        metric:
          type: string
          description: |
            The type of send event counted toward the limit. Must be uppercase.

            **Required** when `is_campaign_audience_limit_enabled` is `true`.
          enum: [SENT]
          example: "SENT"
        frequency:
          type: string
          description: |
            The window over which the limit is applied. Must be uppercase.

            - `TOTAL` - applies the cap across the full lifetime of the campaign. Supported for all delivery types on both Email and Push.
            - `INSTANCE` - applies the cap per campaign instance (for example, per periodic send). **Valid only for Push `PERIODIC` campaigns.** For all other delivery types, use `TOTAL`.

            **Required** when `is_campaign_audience_limit_enabled` is `true`.
          enum: [TOTAL, INSTANCE]
          example: "TOTAL"
        limit:
          type: integer
          minimum: 1
          maximum: 9999999999
          description: |
            The maximum number of users who can receive this campaign (or per instance, when
            `frequency` is `INSTANCE`). Must be between 1 and 9,999,999,999.

            **Required** when `is_campaign_audience_limit_enabled` is `true`.
          example: 100000

    CampaignDetailsResponse:
      type: object
      description: |
        Detailed information about a campaign returned by GET `/v5/campaigns/{campaign_id}` and
        POST `/v5/campaigns/search`.

        **Identifier change from V1 to V5:**
        - In V1, the campaign identifier was returned as `campaign_id`. In V5, it is returned as `id`.
        - The legacy `campaign_id` field may also be present alongside `id` for backward compatibility, but `id` is the canonical V5 identifier.

        **V1 fields preserved in V5 under the same names:** `created_by`, `updated_by`, `created_at`, `updated_at`, `sent_time`, `flow_id`, `flow_name`, `parent_id`, `connector`, `utm_params`, `campaign_audience_limit`.
      properties:
        id:
          type: string
          description: |
            The unique document identifier for this campaign revision.

            In V1, this field was `campaign_id`. In V5, it is `id`.
          example: "64a1b2c3d4e5f6a7b8c9d0e1"
        campaign_id:
          type: string
          description: |
            The canonical campaign identifier shared across all versions of the campaign when
            campaign versioning is enabled. In non-versioned workspaces, `campaign_id` equals `id`.
          example: "camp_abc123xyz"
        version_number: 
          type: integer
          description: |
            Monotonic version index for this campaign document when campaign versioning is enabled. Publishing edits to a previously published campaign creates a new document with a higher `version_number`. `campaign_id` is the canonical identifier shared across versions; `id` is unique per document.
        status:
          type: string
          enum: [DRAFT, ACTIVE, SCHEDULED, PAUSED, SENT, SENDING, STOPPED, ARCHIVED]
          description: The current status of the campaign.
        channel: 
          type: string
          enum: [PUSH, EMAIL, SMS]
          description: The communication channel.
        campaign_delivery_type: 
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED, BROADCAST_LIVE_ACTIVITY]
          description: The delivery type of the campaign.
        created_by: 
          type: string
          format: email
          description: The email ID of the user who created the campaign.
        updated_by: 
          type: string
          format: email
          description: The email ID of the user who last updated the campaign.
        created_at:
          type: string
          description: The timestamp when the campaign was created, in UTC.
          example: "2024-07-04 08:00:54.847000"
        updated_at:
          type: string
          description: The timestamp when the campaign was last updated, in UTC.
        sent_time:
          type: string
          description: The timestamp when the campaign was sent, in UTC.
          example: "2024-07-04 20:32:00"
        flow_id: 
          type: string
          description: |
            The flow ID (only for flow campaigns).
            
            Only applicable when `include_child_campaigns` is true.
        flow_name: 
          type: string
          description: |
            The flow name (only for flow campaigns).
            
            Only applicable when `include_child_campaigns` is true.
        parent_id: 
          type: string
          description: |
            The campaign ID of the parent campaign.
            
            Only applicable for periodic child campaigns when `include_child_campaigns` is true.
        basic_details:
          description: |
            Basic campaign settings - name, tags, team, platform targets, and channel-specific
            identifiers. Structure varies by channel:
            - **Push:** See `PushBasicDetailsV5` schema (name, platforms, tags, team, geofences, etc.)
            - **Email:** See `EmailBasicDetailsV5` schema (name, content_type, subscription_category, user_attribute_identifier, etc.)
            - **SMS:** connector, sender name, and similar fields.
          oneOf:
            - $ref: '#/components/schemas/PushBasicDetailsV5'
            - $ref: '#/components/schemas/EmailBasicDetailsV5'
        campaign_content:
          description: |
            The full campaign content payload including locales and A/B test variations.
            Structure varies by channel:
            - **Push:** See `PushCampaignContent` schema.
            - **Email:** See `EmailCampaignContent` schema.
          oneOf:
            - $ref: '#/components/schemas/PushCampaignContent'
            - $ref: '#/components/schemas/EmailCampaignContent'
        trigger_condition:
          description: |
            Trigger condition for event-triggered campaigns. Structure varies by channel:
            - **Push:** See `PushTriggerCondition` schema (supports INTELLIGENT_DELAY).
            - **Email:** See `EmailTriggerCondition` schema.
          oneOf:
            - $ref: '#/components/schemas/PushTriggerCondition'
            - $ref: '#/components/schemas/EmailTriggerCondition'
        segmentation_details: { $ref: '#/components/schemas/SegmentationDetails' }
        scheduling_details:
          description: |
            Scheduling configuration as resolved on the campaign. The response is a normalized, read-only view and can include fields that are not part of the `SchedulingDetails` request schema:
            - `delivery_type` may be returned as `SCHEDULED` (a resolved response value, not one of the request enum values).
            - `schedule_time` holds the resolved send time and corresponds to the request-side `start_time`.
            - `time_zone` holds the resolved IANA time zone the schedule is interpreted in.

            Treat these as response-only fields. Use the `SchedulingDetails` request schema when constructing create or update payloads.
          allOf:
            - $ref: '#/components/schemas/SchedulingDetails'
        delivery_controls:
          description: |
            Delivery control settings. Structure varies by channel:
            - **Push:** See `PushDeliveryControls` schema (bypass_dnd, campaign_throttle_rpm, ignore_frequency_capping, etc.)
            - **Email:** See `EmailDeliveryControls` schema.
          oneOf:
            - $ref: '#/components/schemas/PushDeliveryControls'
            - $ref: '#/components/schemas/EmailDeliveryControls'
        advanced:
          description: Advanced campaign settings (for Push campaigns). See the `AdvancedDetails` schema for child properties such as `expiration_settings` and `platform_level_priority`.
          allOf:
            - $ref: '#/components/schemas/AdvancedDetails'
        conversion_goal_details: { $ref: '#/components/schemas/ConversionGoalDetails' }
        control_group_details: { $ref: '#/components/schemas/ControlGroupDetails' }
        utm_params: { $ref: '#/components/schemas/UTMParams' }
        connector:
          type: object
          description: |
            Connector configuration (for Email and SMS campaigns).
          properties:
            connector_type: 
              type: string
              example: "SENDGRID"
            connector_name: 
              type: string
              example: "default"
        sender_name: 
          type: string
          description: |
            The sender name configured for the campaign.
            
            Only applicable for SMS campaigns.
        campaign_audience_limit: { $ref: '#/components/schemas/CampaignAudienceLimit' }

    CampaignMetaResponseV5:
      type: object
      description: |
        Campaign metadata including basic information and reachability details.

        **V1 compatibility:**
        - All field names are preserved from the V1 Get Campaign Meta response: `campaign_id`, `campaign_name`, `campaign_status`, `campaign_delivery_type`, `campaign_team`, `campaign_tags`, `campaign_start_time`.
        - No field has been renamed in V5.
        - V5 adds one new field: `version_number` (present only when campaign versioning is enabled).
      properties:
        campaign_id: 
          type: string
          description: The unique ID of the campaign.
          example: "camp_abc123xyz"
        version_number: 
          type: integer
          description: |
            Monotonic version index for this campaign document when campaign versioning is enabled. `campaign_id` is the canonical identifier across versions.
        channel: 
          type: string
          enum: [EMAIL, PUSH, SMS, WHATSAPP, FACEBOOK, 'GOOGLE ADS', CONNECTORS]
          description: The communication channel.
        platform: 
          type: array
          items: 
            type: string
            enum: [ANDROID, IOS, WEB]
          description: The platform types supported for the campaign (applicable for Push).
        created_by: 
          type: string
          format: email
          description: The email ID of the user who created the campaign.
        campaign_delivery_type: 
          type: string
          enum: [ONE_TIME, PERIODIC, EVENT_TRIGGERED, BUSINESS_EVENT_TRIGGERED, DEVICE_TRIGGERED, LOCATION_TRIGGERED, BROADCAST_LIVE_ACTIVITY]
          description: The delivery type of the campaign.
        campaign_name: 
          type: string
          description: The name of the campaign.
          example: "Summer Sale Campaign"
        campaign_team: 
          type: string
          description: The team name associated with the campaign.
        campaign_tags: 
          type: array
          items: { type: string }
          description: Tags associated with the campaign.
        campaign_status: 
          type: string
          enum: [DRAFT, SCHEDULED, ACTIVE, PAUSED, SENT, SENDING, STOPPED, ARCHIVED]
          description: The current status of the campaign.
        campaign_start_time:
          type: string
          format: date-time
          description: The start time of the campaign in ISO 8601 format. This value is returned in UTC.
          example: "2024-11-28T12:18:00"
        parent_campaign_id: 
          type: string
          description: |
            The `id` (24-character ObjectId) of the parent campaign.

            Only shown if the requested `id` belongs to a child campaign and `include_child_campaigns` is true.
        total_child_campaigns: 
          type: integer
          description: |
            The number of child campaigns.
            
            Only shown if the campaign_id belongs to a parent campaign. Only applicable for periodic campaigns.
        rejection_comment:
          type: string
          description: |
            The reason a campaign was rejected during review.
            
            Only present when the campaign's current status is `DRAFT` after a rejection.
        reachability_details:
          type: object
          nullable: true
          description: |
            Reachability information for the campaign.
            
            **Availability:** Only populated for **scheduled** campaigns of these delivery types:
            - One-time scheduled campaigns
            - Business event-triggered campaigns
            - Event-triggered campaigns
            
            For all other campaign types (periodic, device-triggered, location-triggered, etc.), this field is `null`.
            
            Reachability is an estimated value calculated once daily and cached for 24 hours.
            Multiple API calls within the same day return the cached value. The count may vary over
            time as users install or uninstall the app, or change their subscription status.
          properties:
            count:
              type: integer
              description: The estimated number of users the scheduled campaign will reach.
              example: 18182
            last_updated_time:
              type: string
              format: date-time
              description: The timestamp when reachability was last calculated, in ISO 8601 format. This value is returned in UTC.
              example: "2024-11-27T09:05:42.661000"

    V5ValidateResponse:
      type: object
      description: |
        - When `valid` is `true`, only `valid` is returned.
        - When `valid` is `false`, `errors` lists one or more field-level issues found during publish-time validation (`DRAFT_PUBLISH`).
      properties:
        valid:
          type: boolean
          description: "`true` if the campaign passes all publish-time validation checks. `false` if one or more checks fail."
          example: false
        errors:
          type: array
          description: |
            Present only when `valid` is `false`. Each item describes a single validation failure. Multiple errors can be returned, and more than one error can reference the same field.
          items:
            type: object
            properties:
              field:
                type: string
                description: |
                  Dot-separated JSON path to the field that failed validation, relative to the campaign document root (for example, `campaign_content.content.push.android.basic_details.title` or `scheduling_details`).
                example: "campaign_content.content.push.android.basic_details.title"
              issue:
                type: string
                description: Human-readable description of the validation failure for this field. The value is free-form and may vary by channel, delivery type, and component.
                example: "title is required"

    CampaignDraftCreatedData:
      type: object
      required: [id, status]
      description: Payload returned when a draft is successfully created.
      properties:
        id:
          type: string
          description: Raw 24-character campaign ObjectId.
          example: "64a1b2c3d4e5f6a7b8c9d0e1"
        status:
          type: string
          enum: [DRAFT]
          description: Created campaigns are always returned as `DRAFT`.

    CampaignPatchAcceptedData:
      type: object
      required: [id]
      description: >-
        Returned after a successful component PATCH.
      properties:
        id:
          type: string
          description: Raw 24-character campaign ObjectId.
          example: "64a1b2c3d4e5f6a7b8c9d0e1"

    CampaignStatusTransitionData:
      type: object
      required: [id, action]
      description: Returned after a successful status transition on a published campaign.
      properties:
        id:
          type: string
          description: Raw 24-character campaign ObjectId.
          example: "64a1b2c3d4e5f6a7b8c9d0e1"
        action:
          type: string
          description: The action that was applied (matches the requested action).
          enum: [STOP, PAUSE, RESUME]

    CampaignSearchResponseData:
      type: object
      required: [campaigns]
      description: Search and meta list endpoints wrap rows under `data.campaigns`.
      properties:
        campaigns:
          type: array
          items: { $ref: '#/components/schemas/CampaignDetailsResponse' }

    CampaignMetaListResponseData:
      type: object
      description: |
        The response does not include a `total_count` field. To determine whether you have reached the last page, check whether the number of items returned is less than the `limit` you requested. If `campaigns` contains fewer items than `limit`, you have retrieved all results.
      required: [campaigns]
      properties:
        campaigns:
          type: array
          items: { $ref: '#/components/schemas/CampaignMetaResponseV5' }

    TestCampaignResultData:
      type: object
      description: |
        Result of a test send. `data` maps composite keys to per-send status objects. `message` summarizes the overall operation.
      properties:
        data:
          type: object
          description: |
            Each key is a composite string in the format `{PLATFORM}_{locale}_{variation}` identifying a single test send. For example:
            - `ANDROID_default_variation_1` - Android platform, default locale, variation 1.
            - `IOS_en_control` - iOS platform, English locale, control variation.

            Key components:
            - **PLATFORM** - One of `ANDROID`, `IOS`, or `WEB`. For channels that have no platform dimension (such as Email), the channel name is used in this position instead — for example, `EMAIL_default_variation_1`.
            - **locale** - The locale name (for example, `en`, `fr`). Defaults to `default` if no locale is configured on the campaign.
            - **variation** - The variation name (for example, `variation_1`, `control`). Defaults to `variation_1` if no A/B test is configured.

            When you specify `platform`, `locale_name`, or `variation` in `test_campaign_meta`, only the matching key appears in the response.
          additionalProperties:
            type: object
            properties:
              status:
                type: string
                enum: [SENT, FAILED]
                description: Delivery status for this platform-locale-variation combination.
              failure_reason:
                type: string
                description: Present only when `status` is `FAILED`. Describes why the test send failed for this combination.
        message:
          type: string
          description: Human-readable summary of the test send operation.
          example: "Test campaign processed"

  responses:
    V5ValidationError:
      description: Request failed schema or component validation.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          examples:
            validation_failed:
              summary: Generic field validation failure
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  request_id: "req-push-001"
                  details:
                    - target: "campaign_delivery_type"
                      message: "campaign_delivery_type value is required."
            double_opt_in_not_enabled_for_account:
              summary: Double opt-in not enabled for account
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "Double opt-in is not enabled for this account"
                  target: "basic_details"
                  details:
                    - target: "basic_details.send_only_double_opt_in_users"
                      message: "Double opt-in is not enabled for this account"
            double_opt_in_single_opt_in_method:
              summary: Account opt-in method is Single Opt-in
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "send_only_double_opt_in_users cannot be set to true when account opt-in method is Single Opt-in"
                  target: "basic_details"
                  details:
                    - target: "basic_details.send_only_double_opt_in_users"
                      message: "send_only_double_opt_in_users cannot be set to true when account opt-in method is Single Opt-in"
            double_opt_in_used_on_transactional_campaign:
              summary: Used on TRANSACTIONAL campaign
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "send_only_double_opt_in_users is not supported for transactional campaigns"
                  target: "basic_details"
                  details:
                    - target: "basic_details.send_only_double_opt_in_users"
                      message: "send_only_double_opt_in_users is not supported for transactional campaigns"
            double_opt_in_wrong_type:
              summary: Wrong type (non-boolean)
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "send_only_double_opt_in_users - Invalid value passed. Expected type: boolean"
                  target: "basic_details"
                  details:
                    - target: "basic_details.send_only_double_opt_in_users"
                      message: "send_only_double_opt_in_users - Invalid value passed. Expected type: boolean"
            deduplication_attribute_feature_not_enabled_for_account:
              summary: deduplication_attribute - feature not enabled for account
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "Brand dedup is not enabled for this account"
                  target: "basic_details"
                  details:
                    - target: "basic_details.deduplication_attribute"
                      message: "Brand dedup is not enabled for this account"
            deduplication_attribute_invalid_attribute_name:
              summary: deduplication_attribute - invalid attribute name
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "deduplication_attribute '<value>' is not a valid user attribute for this account"
                  target: "basic_details"
                  details:
                    - target: "basic_details.deduplication_attribute"
                      message: "deduplication_attribute '<value>' is not a valid user attribute for this account"
            deduplication_attribute_wrong_type_non_string:
              summary: deduplication_attribute - wrong type (non-string)
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "deduplication_attribute - Invalid value passed. Expected type: string, but got: int."
                  target: "basic_details"
                  details:
                    - target: "basic_details.deduplication_attribute"
                      message: "deduplication_attribute - Invalid value passed. Expected type: string, but got: int."
            deduplication_attribute_not_supported_for_api_triggered:
              summary: deduplication_attribute not supported for API_TRIGGERED campaigns
              value:
                response_id: "abc-101"
                error:
                  code: "BAD_REQUEST"
                  message: "deduplication_attribute is not supported for API_TRIGGERED campaigns"
                  target: "basic_details"
                  details:
                    - target: "basic_details.deduplication_attribute"
                      message: "deduplication_attribute is not supported for API_TRIGGERED campaigns"
            gmail_annotations_on_transactional:
              summary: gmail_annotations used on TRANSACTIONAL campaign
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations"
                      message: "gmail_annotations is not supported for TRANSACTIONAL content type. It is only supported for PROMOTIONAL campaigns."
            gmail_annotations_missing_sender_logo:
              summary: sender_logo missing when gmail_annotations is present
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.sender_logo"
                      message: "sender_logo is required when gmail_annotations is specified"
            gmail_annotations_invalid_sender_logo:
              summary: sender_logo is not a valid HTTPS URL
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.sender_logo"
                      message: "sender_logo must be a valid https URL"
            gmail_annotations_deal_card_missing_field:
              summary: Required deal_card field missing
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.deal_card.availability_starts"
                      message: "availability_starts is required when deal_card is specified"
            gmail_annotations_deal_card_invalid_end_time:
              summary: availability_ends less than 1 hour after availability_starts
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.deal_card"
                      message: "End date and time must be a minimum of one hour later than the start date and time"
            gmail_annotations_product_carousel_minimum_products:
              summary: Product carousel has fewer than 2 products
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel"
                      message: "Product carousel requires a minimum of 2 products"
            gmail_annotations_product_set_product_count_exceeds_maximum:
              summary: product_count exceeds maximum of 9
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.product_set_data"
                      message: "product_count must be between 2 and 9"
            gmail_annotations_product_carousel_invalid_original_price:
              summary: Manual product has invalid original_price
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.manual_data.products[0].original_price"
                      message: "original_price must be a valid number"
            gmail_annotations_product_carousel_invalid_discount_value:
              summary: Manual product has invalid discount_value
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.manual_data.products[0].discount_value"
                      message: "discount_value must be a valid number"
            gmail_annotations_product_carousel_invalid_promo_url:
              summary: Manual product has invalid promo_url
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.manual_data.products[0].promo_url"
                      message: "promo_url must be a valid https URL"
            gmail_annotations_product_carousel_empty_product_image:
              summary: Manual product has empty product_image
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.manual_data.products[0].product_image"
                      message: "product_image (Image URL) cannot be empty"
            gmail_annotations_product_carousel_invalid_product_image_url:
              summary: Manual product has invalid product_image URL
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.manual_data.products[0].product_image"
                      message: "product_image must be a valid https URL"
            gmail_annotations_product_set_not_found:
              summary: Product set ID does not exist in the workspace
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel.product_set_data"
                      message: "product_set not found for the given workspace"
            gmail_annotations_product_sets_feature_not_enabled:
              summary: Product Sets feature is not enabled for the workspace
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.gmail_annotations.product_carousel"
                      message: "Product Sets is not enabled for this workspace"
            link_branding_domain_no_domain_configured:
              summary: link_branding_domain - no link branding domains configured
              value:
                response_id: "abc-101"
                error:
                  code: BAD_REQUEST
                  message: "link_branding_domain cannot be processed because no link branding domains have been configured for this workspace. Please set up a domain in your MoEngage dashboard settings first."
                  target: "campaign_content.content.email"
                  details:
                    - target: "campaign_content.content.email.link_branding_domain"
                      message: "link_branding_domain cannot be processed because no link branding domains have been configured for this workspace."
            link_branding_domain_no_match:
              summary: link_branding_domain - does not match a configured domain
              value:
                response_id: "abc-101"
                error:
                  code: BAD_REQUEST
                  message: "The provided link_branding_domain does not match any of the configured domains for this workspace. Please verify the domain name and try again."
                  target: "campaign_content.content.email"
                  details:
                    - target: "campaign_content.content.email.link_branding_domain"
                      message: "The provided link_branding_domain does not match any of the configured domains for this workspace."
            deep_link_domain_no_domain_configured:
              summary: deep_link_domain - no deep linking domains configured
              value:
                response_id: "abc-101"
                error:
                  code: BAD_REQUEST
                  message: "deep_link_domain cannot be processed because no deep linking domains have been configured for this workspace. Please set up a domain in your MoEngage dashboard settings first."
                  target: "campaign_content.content.email"
                  details:
                    - target: "campaign_content.content.email.deep_link_domain"
                      message: "deep_link_domain cannot be processed because no deep linking domains have been configured for this workspace."
            deep_link_domain_no_match:
              summary: deep_link_domain - does not match a configured domain
              value:
                response_id: "abc-101"
                error:
                  code: BAD_REQUEST
                  message: "The provided deep_link_domain does not match any of the configured domains for this workspace. Please verify the domain name and try again."
                  target: "campaign_content.content.email"
                  details:
                    - target: "campaign_content.content.email.deep_link_domain"
                      message: "The provided deep_link_domain does not match any of the configured domains for this workspace."
            from_address_personalization_not_enabled:
              summary: from_address - personalized value used without EMAIL_FROM_ADDRESS_PERS enabled
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.from_address"
                      message: "Personalized from_address is not supported for this workspace. Remove the personalization token, or ask support to enable EMAIL_FROM_ADDRESS_PERS."
            from_address_invalid_email:
              summary: from_address - not a valid email address
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.from_address"
                      message: "from_address is not a valid email address"
            reply_to_address_invalid_email:
              summary: reply_to_address - not a valid email address
              value:
                response_id: "abc-101"
                error:
                  code: VALIDATION_FAILED
                  message: "One or more fields failed validation."
                  details:
                    - target: "campaign_content.content.email.reply_to_address"
                      message: "reply_to_address is not a valid email address"
    V5Unauthorized:
      description: Authentication failure.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-101"
            error:
              code: UNAUTHORIZED
              message: "Invalid or missing credentials."
              details: []
    V5Forbidden:
      description: The authenticated caller does not have permission to perform this operation.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-450"
            error:
              code: FORBIDDEN
              message: "Caller does not have permission to perform this operation."
              details: []
    V5BadRequest:
      description: Wrong state for the requested operation (for example, PATCH a non-draft campaign, or an Idempotency-Key reused with a different body).
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-301"
            error:
              code: BAD_REQUEST
              message: "Campaign is not in DRAFT state and cannot be patched."
              details: []
    V5UnprocessableEntity:
      description: |
        Body is well-formed but cannot be acted on (e.g. validation failure or an invalid status action).
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-601"
            error:
              code: UNPROCESSABLE_ENTITY
              message: "Draft cannot be published; validation failed."
              details:
                - target: "campaign_content.content.push.android.basic_details.title"
                  message: "title is required"
    V5RateLimited:
      description: Per-app rate limit exceeded. Retry after the window indicated in `Retry-After` (seconds).
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-101"
            error:
              code: RATE_LIMITED
              message: "Rate limit exceeded for app key."
              details: []
    V5InternalError:
      description: Unhandled server-side failure.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
          example:
            response_id: "abc-101"
            error:
              code: INTERNAL_ERROR
              message: "Internal server error."
              details: []

paths:
  /v5/campaigns:
    post:
      operationId: create_draft_campaign_v5
      summary: Create Campaign Draft (V5)
      description: |
        Creates a Push or Email campaign draft. Content, audience, and delivery settings can be included at the time of creation, or added later via `PATCH /v5/campaigns/{campaign_id}`.
      x-mint:
        content: |

          <Info>
          **Component reference pages:**
          - For the full schema of `basic_details` and `campaign_content` per channel, platform, and template type :
            - Android (`BASIC` / `STYLIZED_BASIC` / `SIMPLE_IMAGE_CAROUSEL` / `IMAGE_BANNER_WITH_TEXT` / `TIMER` / `TIMER_WITH_PROGRESS_BAR` / `Custom`)
            - iOS (`BASIC` / `STYLIZED_BASIC` / `SIMPLE_IMAGE_CAROUSEL` / `Custom`)
            - Web `BASIC`
            - Email (`html_content` and `custom_template_id`), see [Campaign content reference](/api/campaigns/campaign-content-reference).
          - For the full schema of `trigger_condition`, `segmentation_details`, `scheduling_details`, `delivery_controls`, `conversion_goal_details`, `control_group_details`, `utm_params`, `campaign_audience_limit`, `advanced`, and `geofences`, see [Audience and delivery reference](/api/campaigns/audience-scheduling-delivery-reference).
          </Info>

          #### Rate Limits
          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Create campaign per minute | The total number of create campaign operations per minute per client allowed is 5. |
          | Create campaign per hour | The total number of create campaign operations per hour per client allowed is 25. |
          | Create campaign per day | The total number of create campaign operations per day per client allowed is 100. |

          ### Campaign Creation Limits

          You can create 5 campaigns per minute, 25 campaigns per hour, and 100 campaigns per day.

          <Note>
          **Notes**
          * Higher limit (Total Calls): The system permits a higher volume of total API calls (for example, 120 calls) to accommodate potential failures or retries.
          * Minimum limit (Campaign Creation Limits): The system maintains a stricter quota for actual successful creations (100 campaigns per day).

          Example: If a client submits 120 requests and 20 fail, they successfully generate exactly 100 campaigns.
          Because the system applies the most restrictive threshold, the 100 successful operations trigger the quota limit, and the system issues a rate limit breach warning regardless of the total API calls made.
          * Breaching the limits will reject the request.
          * Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
          </Note>
      tags:
        - Create Campaigns
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: >-
          Campaign payload. Required top-level fields are `channel`, `campaign_delivery_type`, and `created_by`.
          Optional sections (`basic_details`, `campaign_content`, audience, scheduling, and so on) can be omitted or supplied in full.

          **Note:** Use the tabs below to select your campaign type. The schema adapts based on the selected channel.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignCreateV5Request'
            examples:
              push_one_time:
                summary: Push - ONE_TIME
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            notification_channel: "{{notification_channel}}"
                            title: "{{title}}"
                            message: "{{message}}"
                            default_click_action: "DEEPLINKING"
                            default_click_action_value: "{{deep_link_url}}"
                  scheduling_details:
                    delivery_type: "ASAP"
              push_periodic:
                summary: Push - PERIODIC
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "PERIODIC"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                      - "IOS"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    periodic_details:
                      sending_frequency: "WEEKLY"
                      repeat_frequency: 1
                      repeat_on_days_of_week:
                        - "MONDAY"
                  delivery_controls:
                    limit_send_config:
                      enabled: true
                      max_count: 1
                      window_value: 1
                      window_unit: "DAYS"       
              email_one_time:
                summary: Email - ONE_TIME
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                        email_editor: "Froala Editor"
                  scheduling_details:
                    delivery_type: "ASAP"
              email_event_triggered_delay:
                summary: Email - EVENT_TRIGGERED (with delay)
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "EVENT_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "TRANSACTIONAL"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  trigger_condition:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "{{action_name}}"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
                          condition: "IF"
                    trigger_delay_type: "DELAY"
                    trigger_delay_value: 1
                    trigger_delay_granularity: "HOURS"
                    trigger_relation: "AFTER"
                    trigger_attr: "If Action"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_address}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    expiry_time: "{{expiry_time}}"
              email_with_gmail_annotations_deal_card:
                summary: Email - Gmail Annotations Deal Card (IST +05:30)
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "Exclusive Deal Just for You"
                        preview_text: "Save big with our limited-time offer"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        reply_to_address: "{{reply_to_address}}"
                        html_content: "<!DOCTYPE html><html><body><p>Deal!</p></body></html>"
                        gmail_annotations:
                          send_email_if_personalization_fails: true
                          sender_logo: "{{sender_logo}}"
                          sender_logo_type: "image_url"
                          deal_card:
                            description: "Get 20% off on all orders above $50"
                            discount_code: "SAVE20"
                            availability_starts: "2026-06-01T00:00:00+05:30"
                            availability_ends: "2026-06-30T23:59:59+05:30"
                  scheduling_details:
                    delivery_type: "ASAP"
              email_with_gmail_annotations_product_carousel_manual:
                summary: Email - Gmail Annotations Product Carousel (Manual)
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "Featured Products This Week"
                        preview_text: "Discover our latest deals"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        reply_to_address: "{{reply_to_address}}"
                        html_content: "<!DOCTYPE html><html><body><p>Shop now!</p></body></html>"
                        gmail_annotations:
                          send_email_if_personalization_fails: true
                          sender_logo: "{{sender_logo}}"
                          sender_logo_type: "image_url"
                          product_carousel:
                            type: "MANUAL"
                            manual_data:
                              currency: "{{currency}}"
                              products:
                                - id: "{{id}}"
                                  headline: "{{headline}}"
                                  original_price: "{{original_price}}"
                                  discount_value: "{{discount_value}}"
                                  discount_type: "PERCENT"
                                  promo_url: "{{promo_url}}"
                                  product_image: "{{product_image}}"
                                - id: "{{id}}"
                                  headline: "{{headline}}"
                                  original_price: "{{original_price}}"
                                  discount_value: "{{discount_value}}"
                                  discount_type: "VALUE"
                                  promo_url: "{{promo_url}}"
                                  product_image: "{{product_image}}"
                  scheduling_details:
                    delivery_type: "ASAP"
              email_with_gmail_annotations_product_carousel_product_set:
                summary: Email - Gmail Annotations Product Carousel (Product Set)
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "Picks Curated For You"
                        preview_text: "Personalized recommendations"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        reply_to_address: "{{reply_to_address}}"
                        html_content: "<!DOCTYPE html><html><body><p>Shop now!</p></body></html>"
                        gmail_annotations:
                          send_email_if_personalization_fails: true
                          sender_logo: "{{sender_logo}}"
                          sender_logo_type: "image_url"
                          product_carousel:
                            type: "PRODUCT_SET"
                            product_set_data:
                              product_set: "product_set_12345"
                              image_url: "https://example.com/images/fallback.png"
                              headline: "{{headline}}"
                              promo_url: "{{promo_url}}"
                              product_count: 3
                  scheduling_details:
                    delivery_type: "ASAP"
              push_event_triggered:
                summary: Push - EVENT_TRIGGERED
                value:
                  request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  channel: "PUSH"
                  campaign_delivery_type: "EVENT_TRIGGERED"
                  created_by: "marketer@example.com"
                  basic_details:
                    name: "Cart Abandonment - Push"
                    platforms:
                      - "ANDROID"
                      - "IOS"
                  trigger_condition:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "cart_abandoned"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
                    trigger_delay_type: "DELAY"
                    trigger_delay_value: 1
                    trigger_delay_granularity: "HOURS"
                    trigger_relation: "AFTER"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "Items in your cart"
                            message: "You left something behind. Complete your purchase before it sells out."
                            default_click_action: "DEEPLINKING"
                            default_click_action_value: "myapp://cart"
                        ios:
                          template_type: "BASIC"
                          basic_details:
                            title: "Items in your cart"
                            message: "You left something behind. Complete your purchase before it sells out."
                            default_click_action: "DEEPLINKING"
                            default_click_action_value: "myapp://cart"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "2026-07-01T00:00:00"
                    expiry_time: "2026-12-31T23:59:59"
                  delivery_controls:
                    bypass_dnd: false
                    minimum_delay_between_two_notification_in_hour: 24
                    ignore_global_minimum_delay: false
                    limit_send_config:
                      enabled: true
                      max_count: 1
                      window_value: 1
                      window_unit: "DAYS" 
              push_business_event_triggered:
                summary: Push - BUSINESS_EVENT_TRIGGERED
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "BUSINESS_EVENT_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    business_event: "{{business_event_name}}"
                    platforms:
                      - "ANDROID"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    expiry_time: "{{expiry_time}}"
              push_device_triggered:
                summary: Push - DEVICE_TRIGGERED
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "DEVICE_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                  trigger_condition:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "{{device_trigger_event_name}}"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
                    trigger_delay_type: "ASAP"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                  delivery_controls:
                    max_time_to_show_message_of_same_camapign: "{{max_display_hours}}"
                    expiry_time_of_sync_data_in_hour: "{{sync_expiry_hours}}"
                    send_message_in_offline_mode: true
              push_location_triggered:
                summary: Push - LOCATION_TRIGGERED
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "LOCATION_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                      - "IOS"
                    geofences:
                      name: "{{geofence_name}}"
                      latitude: "{{latitude}}"
                      longitude: "{{longitude}}"
                      radius: "{{radius_meters}}"
                      response_time_value: "5"
                      response_time_granularity: "MINUTES"
                      triggered_at: "ENTRY"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                  delivery_controls:
                    send_limit_value: "{{max_sends_per_user}}"
                    send_limit_granularity_in_hours: "{{send_limit_window_hours}}"
              email_one_time_promotional:
                summary: Email - ONE_TIME promotional
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "SENDGRID"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "ASAP"
              email_one_time_transactional:
                summary: Email - ONE_TIME transactional
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "TRANSACTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "ASAP"
              email_periodic:
                summary: Email - PERIODIC
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "PERIODIC"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    periodic_details:
                      sending_frequency: "WEEKLY"
                      repeat_frequency: 1
                      repeat_on_days_of_week:
                        - "TUESDAY"
                  delivery_controls:
                    limit_send_config:
                      enabled: true
                      max_count: 1
                      window_value: 1
                      window_unit: "DAYS"       
              email_event_triggered:
                summary: Email - EVENT_TRIGGERED
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "EVENT_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "TRANSACTIONAL"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  trigger_condition:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "{{action_name}}"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
                    trigger_delay_type: "ASAP"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_address}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    expiry_time: "{{expiry_time}}"
                  delivery_controls:
                    limit_send_config:
                      enabled: true
                      max_count: 1
                      window_value: 1
                      window_unit: "DAYS"   
              email_business_event_triggered:
                summary: Email - BUSINESS_EVENT_TRIGGERED
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "BUSINESS_EVENT_TRIGGERED"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    business_event: "{{business_event_name}}"
                    content_type: "TRANSACTIONAL"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    expiry_time: "{{expiry_time}}"
              push_web_one_time:
                summary: Push - Web ONE_TIME
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "WEB"
                  campaign_content:
                    content:
                      push:
                        web:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                            redirect_url: "{{redirect_url}}"
                            image_url: "{{image_url}}"
                          buttons:
                            - title: "{{button_label}}"
                              url: "{{button_url}}"
                  scheduling_details:
                    delivery_type: "ASAP"
              push_multi_locale_ab_variation:
                summary: Push - Multi-locale with A/B variation
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "ONE_TIME"
                  created_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                  campaign_content:
                    locales:
                      - "es-ES"
                    variation_details:
                      distribution_type: "MANUAL"
                      no_of_variations: 2
                      manual_distribution_percentage:
                        variation_1: 50
                        variation_2: 50
                    content:
                      default:
                        variation_1:
                          push:
                            android:
                              template_type: "BASIC"
                              basic_details:
                                title: "{{title_v1}}"
                                message: "{{message_v1}}"
                        variation_2:
                          push:
                            android:
                              template_type: "BASIC"
                              basic_details:
                                title: "{{title_v2}}"
                                message: "{{message_v2}}"
                      es-ES:
                        variation_1:
                          push:
                            android:
                              template_type: "BASIC"
                              basic_details:
                                title: "{{title_es_v1}}"
                                message: "{{message_es_v1}}"
                        variation_2:
                          push:
                            android:
                              template_type: "BASIC"
                              basic_details:
                                title: "{{title_es_v2}}"
                                message: "{{message_es_v2}}"
                  scheduling_details:
                    delivery_type: "ASAP"
      responses:
        '201':
          description: Draft created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignDraftCreatedData' }
              example:
                response_id: "abc-101"
                type: "campaign"
                data:
                  id: "64a1b2c3d4e5f6a7b8c9d0e1"
                  status: "DRAFT"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '429':
          $ref: '#/components/responses/V5RateLimited'
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/{campaign_id}:
    get:
      operationId: get_single_campaign_v5
      summary: Get Campaign (V5)
      description: |
        Returns the full configuration and current status of a single campaign by its ID.
      x-mint:
        content: |
          <Note>
          **SMS campaigns:** SMS campaigns are returned by this endpoint; the response `channel` field will be `SMS`. The `connector` and `sender_name` fields carry SMS-specific details. SMS campaigns can be retrieved but must be created and managed through the MoEngage dashboard or V1 APIs in the interim.
          </Note>

          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Get campaign per second | The total number of get campaign requests per second per client allowed is 10. |
          | Get campaign per minute | The total number of get campaign requests per minute per client allowed is 100. |
          | Get campaign per hour | The total number of get campaign requests per hour per client allowed is 6000. |

          <Note>
          **Notes**
          * Breaching the limits will reject the request.
          * Per-hour limits use a rolling window of the last 1 hour.
          </Note>
      x-codeSamples:
        - lang: Shell
          label: Get campaign by ID (cURL)
          source: |
            curl -X GET 'https://api-{dc}.moengage.com/v5/campaigns/{{campaign_id}}' \
              -H 'Authorization: Basic {{auth_token}}' \
              -H 'MOE-APPKEY: {{workspace_id}}' \
              -H 'X-MOE-Request-Id: {{request_id}}'
        - lang: Shell
          label: Get an SMS campaign (cURL)
          source: |
            curl -X GET 'https://api-{dc}.moengage.com/v5/campaigns/{{sms_campaign_id}}' \
              -H 'Authorization: Basic {{auth_token}}' \
              -H 'MOE-APPKEY: {{workspace_id}}' \
              -H 'X-MOE-Request-Id: {{request_id}}'
      tags:
        - Get Campaign Details
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - name: campaign_id
          in: path
          required: true
          description: Raw 24-char ObjectId of the campaign to retrieve.
          schema:
            type: string
          examples:
            get_campaign_by_id:
              summary: Get campaign by ID
              value: "{{campaign_id}}"
            get_sms_campaign:
              summary: Get an SMS campaign
              value: "{{sms_campaign_id}}"
      responses:
        '200':
          description: Campaign details retrieved successfully.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignDetailsResponse' }
              examples:
                scheduled_push_campaign:
                  summary: Scheduled one-time Push campaign (Android + iOS)
                  value:
                    response_id: "abc-100"
                    type: "campaign"
                    data:
                      id: "64a1b2c3d4e5f6a7b8c9d0e1"
                      campaign_id: "64a1b2c3d4e5f6a7b8c9d0e1"
                      status: "SCHEDULED"
                      channel: "PUSH"
                      campaign_delivery_type: "ONE_TIME"
                      created_by: "marketer@example.com"
                      updated_by: "marketer@example.com"
                      created_at: "2024-07-04 08:00:54.847000"
                      updated_at: "2024-07-04 09:15:00.000000"
                      basic_details:
                        name: "Summer Sale Push"
                        platforms: ["ANDROID", "IOS"]
                        tags: ["promotional", "seasonal"]
                        team: "Growth Team"
                      campaign_content:
                        content:
                          push:
                            android:
                              template_type: "BASIC"
                              basic_details:
                                title: "Summer Sale — Up to 50% off"
                                message: "Shop now before the sale ends."
                                default_click_action: "DEEPLINKING"
                                default_click_action_value: "https://example.com/sale"
                            ios:
                              template_type: "BASIC"
                              basic_details:
                                title: "Summer Sale — Up to 50% off"
                                message: "Shop now before the sale ends."
                                default_click_action: "DEEPLINKING"
                                default_click_action_value: "https://example.com/sale"
                      scheduling_details:
                        delivery_type: "SCHEDULED"
                        schedule_time: "2024-11-28T12:00:00"
                        time_zone: "UTC"
                      segmentation_details:
                        included_filters:
                          filter_operator: "and"
                          filters:
                            - filter_type: "custom_segments"
                              name: "segment"
                              id: "seg_abc123"
                      delivery_controls:
                        bypass_dnd: false
                        ignore_frequency_capping: false
                      utm_params:
                        utm_source: "moengage"
                        utm_medium: "push"
                        utm_campaign: "summer_sale_2024"
                active_email_campaign:
                  summary: Active periodic Email campaign (promotional)
                  value:
                    response_id: "abc-101"
                    type: "campaign"
                    data:
                      id: "64a1b2c3d4e5f6a7b8c9d0e2"
                      campaign_id: "64a1b2c3d4e5f6a7b8c9d0e2"
                      status: "ACTIVE"
                      channel: "EMAIL"
                      campaign_delivery_type: "PERIODIC"
                      created_by: "marketer@example.com"
                      updated_by: "marketer@example.com"
                      created_at: "2024-01-01 08:00:00.000000"
                      updated_at: "2024-06-15 10:30:00.000000"
                      basic_details:
                        name: "Weekly Newsletter"
                        content_type: "PROMOTIONAL"
                        subscription_category: "newsletter"
                        user_attribute_identifier: "Email (Standard)"
                        tags: ["newsletter"]
                        team: "Retention Team"
                      connector:
                        connector_type: "SENDGRID"
                        connector_name: "SendgridPrimary"
                      campaign_content:
                        content:
                          email:
                            subject: "Your weekly digest is here"
                            preview_text: "See what's new this week"
                            sender_name: "MoEngage Team"
                            from_address: "hello@example.com"
                            reply_to_address: "support@example.com"
                      scheduling_details:
                        delivery_type: "PERIODIC"
                        frequency: "WEEKLY"
                        schedule_time: "09:00:00"
                        time_zone: "America/New_York"
                      segmentation_details:
                        included_filters:
                          filter_operator: "and"
                          filters:
                            - filter_type: "custom_segments"
                              name: "segment"
                              id: "seg_xyz789"
                      utm_params:
                        utm_source: "moengage"
                        utm_medium: "email"
                        utm_campaign: "weekly_newsletter"
                      campaign_audience_limit:
                        is_campaign_audience_limit_enabled: false
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '404':
          description: The requested campaign does not exist or does not belong to the authenticated workspace.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-404"
                error:
                  code: NOT_FOUND
                  message: "Campaign not found."
                  details: []
        '500':
          $ref: '#/components/responses/V5InternalError'
    patch:
      operationId: patch_draft_campaign_v5
      summary: Update Campaign (V5)
      description: |
        Updates individual components of a campaign draft.
      x-mint:
        content: |
          <Note>
          **Campaign publishing is not yet available in V5.** This endpoint only supports updating draft components. To publish campaigns, use the V1 API at `PATCH /core-services/v1/campaigns/{campaign_id}` in the interim. Publishing via V5 will be available in a future release.
          </Note>

          Send a `PATCH` request without a `status` key in the body.

          The API merges each submitted component into the existing draft, then validates the full merged state (`DRAFT_PATCH`).
          After all individual components pass validation, cross-component checks run on the combined result.

          Only the fields you include in the request body are updated. Fields you omit retain their current values.
          For the full field reference per delivery type, see the [Create Campaign API](/api/campaigns/create-campaign).

          #### Update restrictions by campaign state

          | Campaign state | Editable fields | Non-editable fields |
          | :--- | :--- | :--- |
          | **DRAFT** | All fields | — |
          | **SCHEDULED:** One-time (no instance sent) | All fields | `scheduling_type` |
          | **SCHEDULED:** Periodic or Event-triggered (no instance sent) | All fields | `scheduling_type` |
          | **ACTIVE** | All fields except those listed as non-editable | `trigger_condition`, `segmentation_details`, `conversion_goal_details`, `scheduling_type`, `scheduling_start_date` |
          | **STOPPED** or **ARCHIVED** | — | All fields (no updates allowed) |

          <Note>
          For **Event-triggered campaigns**, updated content is cached and takes up to 30 minutes to take effect.
          </Note>

          #### Rate Limits
          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Update campaign per second | The total number of update campaign requests per second per client allowed is 10. |
          | Update campaign per minute | The total number of update campaign requests per minute per client allowed is 100. |
          | Update campaign per hour | The total number of update campaign requests per hour per client allowed is 6000. |

          <Note>
          **Notes**
          * Breaching the limits will reject the request.
          * Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
          </Note>
      tags:
        - Update Campaigns
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - name: campaign_id
          in: path
          required: true
          description: |
            The unique ID of the campaign to update.

            You can fetch the campaign ID using the [Get Campaign Details API](/api/get-campaign-details/search-campaigns-v5) by filtering with campaign name, channel, or other criteria.
          schema:
            type: string
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: |
          In this payload, you can include the fields you want to update.

          **Notes:** - Pass only the fields you want to update. You don't need to send the complete payload.
          - If updating a field within an object, you must pass the complete object.
          - If updating a nested field, you must pass the complete parent object.
          For example, to update the title of a Push notification, pass the complete campaign_content object.
          - Use the tabs below to select your campaign type. The schema will adapt based on the selected channel.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignPatchV5Request'
            examples:
              push_update_campaign_content:
                summary: Push - Update campaign_content
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{notification_title}}"
                            message: "{{notification_message}}"
              email_update_campaign_content:
                summary: Email - Update campaign_content
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                        "email_editor": "Froala Editor"
              email_update_gmail_annotations:
                summary: Email - Update campaign_content with gmail_annotations
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        from_address: "{{from_email}}"
                        html_content: "{{html_body}}"
                        gmail_annotations:
                          send_email_if_personalization_fails: true
                          sender_logo: "{{sender_logo}}"
                          sender_logo_type: "image_url"
                          deal_card:
                            description: "Get 20% off on all orders above $50"
                            discount_code: "SAVE20"
                            availability_starts: "2026-06-01T00:00:00+05:30"
                            availability_ends: "2026-06-30T23:59:59+05:30"
              push_update_basic_details:
                summary: Push - Update basic_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                      - "IOS"
                    tags:
                      - "{{tag}}"
                    team: "{{team_name}}"
              push_update_segmentation_details:
                summary: Push - Update segmentation_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  segmentation_details:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "{{event_name}}"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
              push_update_scheduling_details:
                summary: Push - Update scheduling_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
                    expiry_time: "{{expiry_time}}"
              push_update_trigger_condition:
                summary: Push - Update trigger_condition
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  trigger_condition:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "actions"
                          action_name: "{{trigger_event_name}}"
                          execution:
                            type: "atleast"
                            count: 1
                          executed: true
                    trigger_delay_type: "DELAY"
                    trigger_delay_value: 30
                    trigger_delay_granularity: "MINUTES"
                    trigger_relation: "AFTER"
              push_update_delivery_controls:
                summary: Push - Update delivery_controls
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  delivery_controls:
                    bypass_dnd: false
                    send_limit_value: "{{max_sends_per_user}}"
                    send_limit_granularity_in_hours: "{{send_limit_window_hours}}"
              push_update_utm_params:
                summary: Push - Update utm_params
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  utm_params:
                    utm_source: "{{utm_source}}"
                    utm_medium: "push"
                    utm_campaign: "{{utm_campaign}}"
              push_update_campaign_audience_limit:
                summary: Push - Update campaign_audience_limit (PERIODIC or EVENT_TRIGGERED)
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  campaign_audience_limit:
                    is_campaign_audience_limit_enabled: true
                    metric: "SENT"
                    frequency: "TOTAL"
                    limit: 100000
              push_update_conversion_goal_details:
                summary: Push - Update conversion_goal_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  conversion_goal_details:
                    attribution_window_in_hours: 36
                    goals:
                      - goal_name: "Goal 1"
                        goal_event_name: "{{conversion_event_name}}"
                        is_primary_goal: true
              email_update_basic_details:
                summary: Email - Update basic_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    subscription_category: "{{subscription_category}}"
                    tags:
                      - "{{tag}}"
              email_update_connector:
                summary: Email - Update connector
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
              email_update_scheduling_details:
                summary: Email - Update scheduling_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  scheduling_details:
                    delivery_type: "AT_FIXED_TIME"
                    start_time: "{{start_time}}"
              email_update_utm_params:
                summary: Email - Update utm_params
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  utm_params:
                    utm_source: "{{utm_source}}"
                    utm_medium: "email"
                    utm_campaign: "{{utm_campaign}}"
              push_update_advanced:
                summary: Push - Update advanced
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  advanced:
                    expiration_settings:
                      expire_notification_after_value: 24
                      expire_notification_after_type: "HOUR"
                    platform_level_priority:
                      ios_specific_priority:
                        apns_priority: "10"
                        interruption_level: "ACTIVE"
                      android_specific_priority:
                        send_with_priority: true
              push_update_control_group_details:
                summary: Push - Update control_group_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  control_group_details:
                    is_campaign_control_group_enabled: true
                    campaign_control_group_percentage: 10
              push_update_basic_details_geofences:
                summary: Push - Update basic_details.geofences
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  basic_details:
                    geofences:
                      name: "{{geofence_name}}"
                      latitude: "{{latitude}}"
                      longitude: "{{longitude}}"
                      radius: "{{radius_meters}}"
                      response_time_value: "5"
                      response_time_granularity: "MINUTES"
                      triggered_at: "ENTRY"
              push_update_platform_specific_details:
                summary: Push - Update platform_specific_details
                value:
                  request_id: "{{request_id}}"
                  updated_by: "{{user_email}}"
                  basic_details:
                    platform_specific_details:
                      android:
                        push_amp_plus_enabled: true
                      ios:
                        send_to_all_eligible_device: true
              email_update_segmentation_details:
                summary: Email - Update segmentation_details
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  segmentation_details:
                    included_filters:
                      filter_operator: "and"
                      filters:
                        - filter_type: "user_attributes"
                          data_type: "string"
                          name: "city"
                          operator: "in"
                          value: "{{city_value}}"
              email_update_delivery_controls:
                summary: Email - Update delivery_controls
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  delivery_controls:
                    bypass_dnd: false
                    campaign_throttle_rpm: 2000
                    count_for_frequency_capping: true
              email_update_conversion_goal_details:
                summary: Email - Update conversion_goal_details
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  conversion_goal_details:
                    attribution_window_in_hours: 36
                    goals:
                      - goal_name: "Goal 1"
                        goal_event_name: "{{conversion_event_name}}"
                        is_primary_goal: true
              email_update_control_group_details:
                summary: Email - Update control_group_details
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  control_group_details:
                    is_campaign_control_group_enabled: true
                    campaign_control_group_percentage: 10
              email_update_campaign_audience_limit:
                summary: Email - Update campaign_audience_limit (PERIODIC or EVENT_TRIGGERED)
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  campaign_delivery_type: "{{campaign_delivery_type}}"
                  updated_by: "{{user_email}}"
                  campaign_audience_limit:
                    is_campaign_audience_limit_enabled: true
                    metric: "SENT"
                    frequency: "TOTAL"
                    limit: 100000
      responses:
        '200':
          description: |
            Draft updated successfully.

            **Note:** V1 returned `204 No Content` for successful updates. V5 returns `200 OK` with a
            response body. Update any integration that checks for `204` to expect `200` instead.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignPatchAcceptedData' }
              examples:
                component_updated:
                  summary: Component update accepted
                  value:
                    response_id: "abc-301"
                    type: "campaign"
                    data:
                      id: "64a1b2c3d4e5f6a7b8c9d0e1"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '403':
          description: |
            The caller does not have the required permission to update this campaign.
            This occurs when the API credential's scope does not include campaign update rights,
            or when the caller attempts to update a campaign owned by a different team with restricted access.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-403"
                error:
                  code: FORBIDDEN
                  message: "Caller does not have permission to update this campaign."
                  details: []
        '409':
          description: |
            The campaign is not in a state that allows updates.
            This occurs when the campaign is not in `DRAFT` state - for example, it is `ACTIVE`, `PAUSED`,
            or `STOPPED`. Only `DRAFT` campaigns can be updated via this endpoint.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-409"
                error:
                  code: BAD_REQUEST
                  message: "Campaign is not in DRAFT state and cannot be patched."
                  details: []
        '429':
          description: |
            The per-app rate limit for this endpoint has been exceeded. Retry after the window
            indicated in the `Retry-After` response header (in seconds).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-429"
                error:
                  code: RATE_LIMITED
                  message: "Rate limit exceeded. Retry after 60 seconds."
                  details: []
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/{campaign_id}/validate:
    post:
      operationId: validate_draft_campaign_v5
      summary: Validate Campaign (V5)
      description: |
        Runs full publish-time validation (`DRAFT_PUBLISH`) on a saved draft without mutating it. Returns `valid: true` if the campaign would pass, or a list of blocking errors if it would not.

        All campaign data is read from the saved draft identified by `campaign_id` in the path. There is no channel-specific payload.
      x-mint:
        content: |
          <Info>
          You do not need an idempotency key for this endpoint. The validate endpoint is read-only and safe to retry freely.
          </Info>

          ### Validation Modes

          The V5 API applies validation in two modes:

          | Mode | Triggered by | Strictness |
          | :--- | :--- | :--- |
          | Lenient | Draft create (`POST /v5/campaigns`) and component patch (`PATCH /v5/campaigns/{id}`) | Partial — individual components are validated in isolation as they are written |
          | Full (`DRAFT_PUBLISH`) | `POST /v5/campaigns/{id}/validate` and publish | Strict — all components are validated together as a complete, publish-ready campaign |

          Use this endpoint to catch `DRAFT_PUBLISH` failures before triggering a publish.

          ### Validation Failures

          All validation failures are blocking. There is no warning or non-blocking tier. A campaign that fails validation returns `valid: false` with a list of field-level errors. Each error identifies the `field` path and the `issue`.

          ### Channel-Specific Rules

          The following rules are enforced at `DRAFT_PUBLISH` for each channel:

          **Push**
          - `campaign_content` must be present and include content for all platforms listed in `basic_details.platforms`.
          - `template_type` must be valid for the target platform (Android, iOS, Web).
          - Platform-specific required fields (for example, `title` and `message` for Android basic templates) must be non-empty.

          **Email**
          - `connector.connector_type` and `connector.connector_name` must be present and match a connector configured in your workspace.
          - `basic_details.from_address` must be set and match a verified sender in the configured connector.
          - `basic_details.subscription_category` must be present for `PROMOTIONAL` content type.
          - `campaign_content` must include at least one variation with a non-empty `subject` and `html_content`.

          ### Delivery-Type-Specific Rules

          The following rules are enforced at `DRAFT_PUBLISH` based on `campaign_delivery_type`:

          | Delivery Type | Additional Requirements |
          | :--- | :--- |
          | `ONE_TIME`, `PERIODIC` | `scheduling_details` must be present. |
          | `AT_FIXED_TIME` scheduling | `scheduling_details.timezone` must be present. |
          | `PERIODIC` | `scheduling_details.periodic_details` must be present with a valid `sending_frequency`. |
          | `EVENT_TRIGGERED` | `trigger_condition` must be present with at least one `included_filters` entry. |
          | `DEVICE_TRIGGERED` | `trigger_condition` must be present. Trigger delay fields (`trigger_delay_type`, `trigger_delay_value`, `trigger_delay_granularity`) must **not** be set — they are only valid for `EVENT_TRIGGERED`. |
          | `BUSINESS_EVENT_TRIGGERED` | `basic_details.business_event` must be present. |
          | `LOCATION_TRIGGERED` | `basic_details.geofences` must contain at least one valid geofence entry. |

          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Validate campaign per second | The total number of validate campaign requests per second per client allowed is 10. |
          | Validate campaign per minute | The total number of validate campaign requests per minute per client allowed is 100. |
          | Validate campaign per hour | The total number of validate campaign requests per hour per client allowed is 6000. |
      tags:
        - Create Campaigns
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - name: campaign_id
          in: path
          required: true
          description: |
            Raw 24-char ObjectId of the campaign to validate. Use the `id` value returned in the Create Campaign response.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                request_id:
                  type: string
                  description: >-
                    Optional string (UUID v4 recommended) used to identify this validation call.
                    Unlike other POST endpoints, this field is not required and does not enforce deduplication - the validate endpoint is read-only and safe to retry freely.
                  example: "{{request_id}}"
            examples:
              validate:
                summary: Validate a draft campaign
                value:
                  request_id: "{{request_id}}"
      responses:
        '200':
          description: Validation result - always returned regardless of whether the campaign passes or fails validation.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/V5ValidateResponse' }
              examples:
                valid:
                  summary: Campaign passes validation
                  value:
                    response_id: "abc-501"
                    type: "campaign"
                    data:
                      valid: true
                invalid:
                  summary: Campaign fails validation
                  value:
                    response_id: "abc-501"
                    type: "campaign"
                    data:
                      valid: false
                      errors:
                        - field: "campaign_content.content.push.android.basic_details.title"
                          issue: "title is required"
                        - field: "scheduling_details"
                          issue: "scheduling_details is required for publish"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/{campaign_id}/status:
    patch:
      operationId: change_campaign_status_v5
      summary: Update Campaign Status (V5)
      description: |
        Applies a state transition to a published campaign. Supported for **Email** and **Push** campaigns.
      x-mint:
        content: |
          <Note>
          This endpoint only handles post-publish lifecycle transitions (STOP, PAUSE, RESUME) for campaigns that are already live. Campaign publishing is not yet supported in V5.
          </Note>

          <Note>
          The response does not include the resulting campaign status. To confirm the new state after a transition, call `GET /v5/campaigns/{campaign_id}`.
          </Note>

          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | change campaign status per second | The total number of change campaign status requests per second per client is 10. |
          | change campaign status per minute | The total number of change campaign status requests per minute per client allowed is 100. |
          | change campaign status per hour | The total number of change campaign status requests per hour per client allowed is 6000. |

          <Note>
          **Notes**
          * Breaching the limits will reject the request.
          * Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
          </Note>
      tags:
        - Update Campaigns
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - name: campaign_id
          in: path
          required: true
          description: Raw 24-char ObjectId of the published campaign to transition.
          schema:
            type: string
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: Send campaign status change request with action.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignStatusV5Request'
            examples:
              stop_campaign:
                summary: Stop a campaign
                value:
                  request_id: "{{request_id}}"
                  action: "STOP"
              pause_campaign:
                summary: Pause a campaign
                value:
                  request_id: "{{request_id}}"
                  action: "PAUSE"
              resume_campaign:
                summary: Resume a campaign
                value:
                  request_id: "{{request_id}}"
                  action: "RESUME"
      responses:
        '200':
          description: Transition accepted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignStatusTransitionData' }
              example:
                response_id: "abc-601"
                type: "campaign"
                data:
                  id: "64a1b2c3d4e5f6a7b8c9d0e1"
                  action: "STOP"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '422':
          description: |
            The action is not valid for the campaign's current state or delivery type. Common causes:
            - Attempting `STOP` on a Periodic campaign.
            - Attempting `PAUSE` or `RESUME` on a One-time campaign.
            - Attempting `RESUME` on a campaign that is not in `PAUSED` state.
            - Attempting `PAUSE` on a campaign that is not in `ACTIVE`, `SCHEDULED`, or `SENDING` state.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-422"
                error:
                  code: UNPROCESSABLE_ENTITY
                  message: "Action STOP is not valid for a PERIODIC campaign."
                  details: []
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/search:
    post:
      operationId: search_campaigns_v5
      summary: Search Campaigns (V5)
      description: |
        Returns the full V5 campaign payload for all campaigns that match the specified filters.
        Supports pagination and filtering by channel, status, tags, name, IDs, dates, delivery
        type, and creator.
      x-mint:
        content: |
          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Search campaign per second | The total number of search campaign requests per second per client allowed is 10. |
          | Search campaign per minute | The total number of search campaign requests per minute per client allowed is 100. |
          | Search campaign per hour | The total number of search campaign requests per hour per client allowed is 6000. |

          <Note>
          **Notes**
          * Breaching the limits will reject the request.
          * Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
          </Note>
      tags:
        - Get Campaign Details
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: |
          Provide the search criteria and pagination parameters for finding campaigns.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchV5Request'
            examples:
              filter_by_channel:
                summary: Filter by channel
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "EMAIL"
              filter_by_delivery_type:
                summary: Filter by delivery type
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    campaign_delivery_type:
                      - "ONE_TIME"
              filter_by_creation_date_range:
                summary: Filter by creation date range
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    created_date:
                      from_date: "{{from_date}}"
                      to_date: "{{to_date}}"
              filter_by_campaign_id:
                summary: Filter by campaign ID
                value:
                  request_id: "{{request_id}}"
                  limit: 1
                  page: 1
                  campaign_fields:
                    ids:
                      - "{{campaign_id}}"
              filter_by_campaign_name:
                summary: Filter by campaign name
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    name: "{{campaign_name_partial}}"
              filter_by_creator:
                summary: Filter by creator
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    created_by:
                      - "{{creator_email}}"
              filter_multiple:
                summary: Filter with multiple fields combined
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "PUSH"
                    campaign_delivery_type:
                      - "ONE_TIME"
                    status:
                      - "ACTIVE"
                      - "SCHEDULED"
                    created_by:
                      - "{{creator_email}}"
                    created_date:
                      from_date: "{{from_date}}"
                      to_date: "{{to_date}}"
                    tags:
                      - "{{tag_name}}"
              include_archived:
                summary: Include archived campaigns
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "PUSH"
                      - "EMAIL"
                  include_archive_campaigns: true
      responses:
        '200':
          description: Campaigns matching the filter criteria.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignSearchResponseData' }
              examples:
                push_results:
                  summary: Push campaign results
                  value:
                    response_id: "resp-search-001"
                    type: "campaign"
                    data:
                      campaigns:
                        - id: "64a1b2c3d4e5f6a7b8c9d0e1"
                          campaign_id: "64a1b2c3d4e5f6a7b8c9d0e1"
                          status: "ACTIVE"
                          channel: "PUSH"
                          campaign_delivery_type: "PERIODIC"
                          created_by: "marketer@example.com"
                          created_at: "2024-01-01 08:00:00.000000"
                          updated_at: "2024-06-01 10:00:00.000000"
                          basic_details:
                            name: "Weekly Engagement Push"
                            platforms: ["ANDROID", "IOS"]
                            tags: ["engagement"]
                            team: "Growth Team"
                email_results:
                  summary: Email campaign results
                  value:
                    response_id: "resp-search-002"
                    type: "campaign"
                    data:
                      campaigns:
                        - id: "64a1b2c3d4e5f6a7b8c9d0e2"
                          campaign_id: "64a1b2c3d4e5f6a7b8c9d0e2"
                          status: "SCHEDULED"
                          channel: "EMAIL"
                          campaign_delivery_type: "ONE_TIME"
                          created_by: "marketer@example.com"
                          created_at: "2024-07-04 08:00:54.847000"
                          updated_at: "2024-07-04 09:15:00.000000"
                          basic_details:
                            name: "Summer Sale Email"
                            content_type: "PROMOTIONAL"
                            subscription_category: "promotions"
                            user_attribute_identifier: "Email (Standard)"
                            tags: ["promotional", "seasonal"]
                          connector:
                            connector_type: "SENDGRID"
                            connector_name: "SendgridPrimary"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/meta:
    post:
      operationId: get_campaign_meta_v5
      summary: Get Campaign Meta (V5)
      description: |
        Returns lightweight metadata for one or more campaigns without loading their full configuration.
      x-mint:
        content: |
          #### Supported Channels
          - Email
          - Push
          - SMS
          - WhatsApp
          - Facebook
          - Google Ads
          - Connectors

          #### Reachability Information
          - Available only for **scheduled** campaigns (one-time, business event-triggered, and event-triggered).
          - Provides estimated user count that will receive the campaign.
          - Calculated once daily and cached for 24 hours. Multiple API calls within the same day return the cached value.
          - May vary due to app installations/uninstalls or subscription changes.

          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | get campaign meta per second | The total number of get campaign meta requests per second per client allowed is 10. |
          | get campaign meta per minute | The total number of get campaign meta requests per minute per client allowed is 100. |
          | get campaign meta per hour | The total number of get campaign meta requests per hour per client allowed is 6000. |

          <Note>
          **Notes**
          * Breaching the limits will reject the request.
          * Per-hour and per-day limits use a rolling window of the last 1 hour and last 24 hours respectively.
          </Note>
      tags:
        - Get Campaign Details
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: |
          Provide the search criteria for retrieving campaign metadata and reachability information.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MetaV5Request'
            examples:
              filter_by_id_and_status:
                summary: Filter by ID and status
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    id: "{{campaign_id}}"
                    status:
                      - "SCHEDULED"
                      - "ACTIVE"
                      - "PAUSED"
                      - "DRAFT"
              filter_by_channel_and_date:
                summary: Filter by channel and date range
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "PUSH"
                      - "EMAIL"
                    created_date:
                      from_date: "2024-01-01T00:00:00"
                      to_date: "2024-12-31T23:59:59"
              filter_by_delivery_type_and_creator:
                summary: Filter by delivery type and creator
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    delivery_type: "ONE_TIME"
                    created_by:
                      - "{{creator_email}}"
                    name: "{{campaign_name_partial}}"
                    tags:
                      - "promotional"
                  include_child_campaigns: false
              with_child_counts:
                summary: With child campaign counts
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "PUSH"
                      - "EMAIL"
                    campaign_delivery_type:
                      - "PERIODIC"
                      - "BUSINESS_EVENT_TRIGGERED"
                  include_child_campaigns: true
              reachability:
                summary: Reachability for a scheduled campaign
                value:
                  request_id: "{{request_id}}"
                  limit: 1
                  page: 1
                  campaign_fields:
                    id: "{{campaign_id}}"
                    status:
                      - "SCHEDULED"
              filter_sms_whatsapp:
                summary: Filter SMS and WhatsApp
                value:
                  request_id: "{{request_id}}"
                  limit: 15
                  page: 1
                  campaign_fields:
                    channels:
                      - "SMS"
                      - "WHATSAPP"
                    status:
                      - "ACTIVE"
      responses:
        '200':
          description: Campaign meta rows
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/CampaignMetaListResponseData' }
              examples:
                scheduled_push_with_reachability:
                  summary: Scheduled Push campaign with reachability details
                  value:
                    response_id: "resp-meta-001"
                    type: "campaign"
                    data:
                      campaigns:
                        - campaign_id: "64a1b2c3d4e5f6a7b8c9d0e1"
                          channel: "PUSH"
                          platform: ["ANDROID", "IOS"]
                          created_by: "marketer@example.com"
                          campaign_delivery_type: "ONE_TIME"
                          campaign_name: "Summer Sale Campaign"
                          campaign_team: "Growth Team"
                          campaign_tags: ["promotional", "seasonal"]
                          campaign_status: "SCHEDULED"
                          campaign_start_time: "2024-11-28T12:18:00"
                          reachability_details:
                            count: 18182
                            last_updated_time: "2024-11-27T09:05:42.661000"
                periodic_with_child_campaigns:
                  summary: Periodic campaign with child campaign counts
                  value:
                    response_id: "resp-meta-002"
                    type: "campaign"
                    data:
                      campaigns:
                        - campaign_id: "64a1b2c3d4e5f6a7b8c9d0e2"
                          channel: "EMAIL"
                          platform: []
                          created_by: "marketer@example.com"
                          campaign_delivery_type: "PERIODIC"
                          campaign_name: "Weekly Newsletter"
                          campaign_team: "Retention Team"
                          campaign_tags: ["newsletter"]
                          campaign_status: "ACTIVE"
                          campaign_start_time: "2024-01-01T08:00:00"
                          total_child_campaigns: 12
                          reachability_details: null
        '400':
          description: Request failed schema or field validation.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              examples:
                missing_request_id:
                  summary: Missing request_id
                  value:
                    response_id: "resp-meta-400-a"
                    error:
                      code: VALIDATION_FAILED
                      message: "One or more fields failed validation."
                      request_id: ""
                      details:
                        - target: "request_id"
                          message: "request_id is required."
                missing_limit:
                  summary: Missing limit
                  value:
                    response_id: "resp-meta-400-b"
                    error:
                      code: VALIDATION_FAILED
                      message: "One or more fields failed validation."
                      request_id: "{{request_id}}"
                      details:
                        - target: "limit"
                          message: "limit is required."
                missing_page:
                  summary: Missing page
                  value:
                    response_id: "resp-meta-400-c"
                    error:
                      code: VALIDATION_FAILED
                      message: "One or more fields failed validation."
                      request_id: "{{request_id}}"
                      details:
                        - target: "page"
                          message: "page is required."
                feature_not_enabled:
                  summary: Feature not enabled for workspace
                  value:
                    response_id: "resp-meta-400-d"
                    error:
                      code: VALIDATION_FAILED
                      message: "Campaign metadata API is not enabled for this workspace."
                      request_id: "{{request_id}}"
                      details: []
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/campaigns/test:
    post:
      operationId: test_campaign_v5
      summary: Test Campaign (V5)
      description: |
        Sends a test Push or Email message to specific users or device identifiers before publishing the campaign.
      x-mint:
        content: |
          The endpoint supports two modes:

          **Inline mode**
          - Send `channel` and `campaign_content` in the request.
          - For **EMAIL** campaigns, also include `basic_details` and `connector`.
          - Nothing is stored on the server.

          **Draft mode**
          - Send `draft_id` to load content from a saved **DRAFT**.
          - By default, the server sends one test per platform, locale, and variation.
          - To narrow the send, use `test_campaign_meta.platform`, `locale_name`, or `variation`.

          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Test campaign per second | The total number of test campaign requests per second per client allowed is 10. |
          | Test campaign per minute | The total number of test campaign requests per minute per client allowed is 100. |
          | Test campaign per hour | The total number of test campaign requests per hour per client allowed is 6000. |
      tags:
        - Test Campaigns
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
        - $ref: '#/components/parameters/Idempotency-Key-Required'
      requestBody:
        description: >-
          Test campaign configuration including content and target test users.

          **Note:** Use the tabs below to select your campaign type. The schema will adapt based on the selected channel.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestV5Request'
            examples:
              push_inline:
                summary: Push - Inline
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  basic_details:
                    name: "{{campaign_name}}"
                    platforms:
                      - "ANDROID"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "{{title}}"
                            message: "{{message}}"
                            notification_channel: "{{notification_channel}}"
                  test_campaign_meta:
                    identifier: "USER_ATTRIBUTE_UNIQUE_ID"
                    identifier_values:
                      - "{{identifier_value}}"
              email_inline:
                summary: Email - Inline
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  basic_details:
                    name: "{{campaign_name}}"
                    content_type: "PROMOTIONAL"
                    user_attribute_identifier: "Email (Standard)"
                    subscription_category: "{{subscription_category}}"
                  connector:
                    connector_type: "{{connector_type}}"
                    connector_name: "{{connector_name}}"
                  campaign_content:
                    content:
                      email:
                        subject: "{{email_subject}}"
                        sender_name: "{{sender_name}}"
                        preview_text: "{{preview_text}}"
                        from_address: "{{from_email}}"
                        reply_to_address: "{{reply_to_address}}"
                        html_content: "{{html_body}}"
                        email_editor: "Froala Editor"
                  test_campaign_meta:
                    identifier: "EMAIL"
                    identifier_values:
                      - "{{recipient_email}}"
              push_draft:
                summary: Push - Draft mode
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  draft_id: "{{campaign_id}}"
                  test_campaign_meta:
                    identifier: "USER_ATTRIBUTE_UNIQUE_ID"
                    identifier_values:
                      - "{{identifier_value}}"
                    platform: "ANDROID"
              email_draft:
                summary: Email - Draft mode
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  draft_id: "{{campaign_id}}"
                  test_campaign_meta:
                    identifier: "EMAIL"
                    identifier_values:
                      - "{{recipient_email}}"
      responses:
        '200':
          description: Test send result
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/V5SuccessEnvelope'
                  - type: object
                    properties:
                      data: { $ref: '#/components/schemas/TestCampaignResultData' }
              examples:
                push_sent:
                  summary: Push test sent successfully
                  value:
                    response_id: "abc-701"
                    type: "campaign"
                    data:
                      data:
                        ANDROID_default_variation_1:
                          status: "SENT"
                      message: "Test campaign processed"
                email_sent:
                  summary: Email test sent successfully
                  value:
                    response_id: "abc-702"
                    type: "campaign"
                    data:
                      data:
                        EMAIL_default_variation_1:
                          status: "SENT"
                      message: "Test campaign processed"
                push_failed:
                  summary: Push test with one failed send
                  value:
                    response_id: "abc-702"
                    type: "campaign"
                    data:
                      data:
                        ANDROID_default_variation_1:
                          status: "FAILED"
                          failure_reason: "Invalid push token"
                      message: "Test campaign processed"
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '409':
          description: >-
            Conflict - the `Idempotency-Key` was already used with a different request body, or a test send
            is already in progress for this key. Retry with a new `Idempotency-Key`.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/V5ErrorEnvelope' }
              example:
                response_id: "abc-701"
                error:
                  code: BAD_REQUEST
                  message: "Idempotency-Key reused with a different request body."
                  details: []
        '500':
          $ref: '#/components/responses/V5InternalError'

  /v5/personalization/preview:
    post:
      x-excluded: true
      operationId: preview_personalized_content_v5
      summary: Personalized Preview (V5)
      description: |
        Returns a fully resolved preview of personalised campaign content for a specific user - all user attributes, event attributes, content blocks, content APIs, product sets, and custom templates are rendered exactly as they would appear to that user at send time.
      x-mint:
        content: |
          #### Rate Limits

          | Rate Limit Name | Rate Limit |
          | :--- | :--- |
          | Personalized preview per minute | 10,000 requests per minute |
      tags:
        - Personalized Preview
      parameters:
        - $ref: '#/components/parameters/MOE-APPKEY'
        - $ref: '#/components/parameters/X-MOE-Request-Id'
      requestBody:
        description: >-
          Personalization preview request. Supply the target user identification and the campaign content to resolve.
          All Jinja expressions and dynamic sources are evaluated against the specified user's profile.
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [request_id, channel, user_identification, campaign_content]
              properties:
                request_id:
                  type: string
                  description: Unique identifier for this request. Correlates with `response_id`.
                channel:
                  type: string
                  enum: [PUSH, EMAIL, SMS]
                  description: Channel of the content to preview.
                user_identification:
                  type: object
                  description: Identifies the target user whose attributes are used to resolve personalisation.
                  required: [type, value]
                  properties:
                    type:
                      type: string
                      enum: [USER_ATTRIBUTE_UNIQUE_ID, EMAIL, MOE_GAID, PUSH_ID]
                      description: Identifier type.
                    value:
                      type: string
                      description: Value of the identifier.
                event_attributes:
                  type: object
                  description: >-
                    Optional map of event attribute key-value pairs to inject into personalisation resolution.
                    Useful for previewing event-triggered campaigns where the triggering event carries attributes.
                  additionalProperties:
                    type: string
                campaign_content:
                  type: object
                  description: >-
                    The campaign content to resolve. Follows the same `campaign_content` structure as the
                    Create Campaign request. Only the content sent here is evaluated - no saved draft is loaded.
                  example:
                    request_id: "preview-001"
                    channel: "PUSH"
                    user_identification:
                      type: "USER_ATTRIBUTE_UNIQUE_ID"
                      value: "user_abc123"
                    event_attributes:
                      product_name: "Running Shoes"
                      product_price: "4999"
                    campaign_content:
                      content:
                        push:
                          android:
                            template_type: "BASIC"
                            basic_details:
                              title: "Hello, {{ user.first_name }}!"
                              message: "{{ event.product_name }} is waiting for you."
            examples:
              push_preview:
                summary: Push personalized preview
                value:
                  request_id: "{{request_id}}"
                  channel: "PUSH"
                  user_identification:
                    type: "USER_ATTRIBUTE_UNIQUE_ID"
                    value: "{{user_id}}"
                  campaign_content:
                    content:
                      push:
                        android:
                          template_type: "BASIC"
                          basic_details:
                            title: "Hello, {{ user.first_name }}!"
                            message: "{{ event.product_name }} is waiting for you."
                  event_attributes:
                    product_name: "{{product_name}}"
              email_preview:
                summary: Email personalized preview
                value:
                  request_id: "{{request_id}}"
                  channel: "EMAIL"
                  user_identification:
                    type: "EMAIL"
                    value: "{{user_email}}"
                  campaign_content:
                    content:
                      email:
                        subject: "Hello, {{ user.first_name }}!"
                        html_content: "<p>Your order {{ event.order_id }} has shipped.</p>"
                  event_attributes:
                    order_id: "{{order_id}}"
      responses:
        '200':
          description: Preview resolved successfully. Returns the fully rendered content for the specified user.
          content:
            application/json:
              schema:
                type: object
                properties:
                  response_id:
                    type: string
                  type:
                    type: string
                    example: "personalization"
                  data:
                    type: object
                    properties:
                      personalized_content:
                        type: object
                        description: >-
                          The resolved campaign content with all Jinja expressions, user attributes,
                          event attributes, and dynamic sources substituted.
                        example:
                          response_id: "preview-001"
                          type: "personalization"
                          data:
                            personalized_content:
                              content:
                                push:
                                  android:
                                    template_type: "BASIC"
                                    basic_details:
                                      title: "Hello, Jane!"
                                      message: "Running Shoes is waiting for you."
        '400':
          $ref: '#/components/responses/V5ValidationError'
        '401':
          $ref: '#/components/responses/V5Unauthorized'
        '429':
          $ref: '#/components/responses/V5RateLimited'
        '500':
          $ref: '#/components/responses/V5InternalError'
