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

# Send Marketing Agent message

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Sends a message to the app's Marketing Agent and starts the turn that answers it. During setup the message answers or revises the step the agent is on. After setup it's an ordinary conversation about the app's marketing.

A message sent before setup has started opens it, the same way [Start Marketing Agent setup](/api-reference/start-marketing-agent-setup) does.

The work runs in the background after the call returns. Poll [Get Marketing Agent state](/api-reference/get-marketing-agent-state) until `status` is `ready` or `error`. If `competitor_scan_status` is `running`, keep polling until it is no longer `running`. The reply appears in the app's chat, which you read with [Read conversation messages](/api-reference/read-conversation-messages).

The agent takes one turn at a time, so a message sent while it's working is rejected. Send it again once `status` is `ready` or `error`. If writing the strategy failed partway, a message starts writing it again and is itself rejected until that finishes.

Set `message_id` to a UUID of your own, or send an `Idempotency-Key` header instead. A retry that carries the same `message_id` or key returns the current state without starting or charging a second turn.

Each run is a Superagent turn, charged to the app's workspace in message credits based on the tokens it uses. A run can take several minutes and is stopped after 15. If the workspace is out of credits the run doesn't start, and `last_error` says so.

This is limited to 120 requests a minute per app for each workspace's personal API keys, so every key in a workspace shares one allowance, across every Marketing Agent endpoint. Some workspaces have a different limit.

<Note>This endpoint accepts a personal API key belonging to a user with editor access to the app and an editor role in its workspace. Read-only keys and workspace API keys are refused.</Note>

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time. Send only the fields documented here. Other request fields are not supported and their behavior can change.</Warning>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/cmo/messages
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - PersonalAccessTokenAuth: []
paths:
  /api/apps/{app_id}/cmo/messages:
    post:
      summary: Send Marketing Agent message
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Sends a message to the app's Marketing Agent and starts the turn that
        answers it. During setup the message answers or revises the step the
        agent is on. After setup it's an ordinary conversation about the app's
        marketing.


        A message sent before setup has started opens it, the same way [Start
        Marketing Agent setup](/api-reference/start-marketing-agent-setup) does.


        The work runs in the background after the call returns. Poll [Get
        Marketing Agent state](/api-reference/get-marketing-agent-state) until
        `status` is `ready` or `error`. If `competitor_scan_status` is
        `running`, keep polling until it is no longer `running`. The reply
        appears in the app's chat, which you read with [Read conversation
        messages](/api-reference/read-conversation-messages).


        The agent takes one turn at a time, so a message sent while it's working
        is rejected. Send it again once `status` is `ready` or `error`. If
        writing the strategy failed partway, a message starts writing it again
        and is itself rejected until that finishes.


        Set `message_id` to a UUID of your own, or send an `Idempotency-Key`
        header instead. A retry that carries the same `message_id` or key
        returns the current state without starting or charging a second turn.


        Each run is a Superagent turn, charged to the app's workspace in message
        credits based on the tokens it uses. A run can take several minutes and
        is stopped after 15. If the workspace is out of credits the run doesn't
        start, and `last_error` says so.


        This is limited to 120 requests a minute per app for each workspace's
        personal API keys, so every key in a workspace shares one allowance,
        across every Marketing Agent endpoint. Some workspaces have a different
        limit.


        <Note>This endpoint accepts a personal API key belonging to a user with
        editor access to the app and an editor role in its workspace. Read-only
        keys and workspace API keys are refused.</Note>


        <Warning>The response includes fields beyond the ones documented here.
        Don't rely on undocumented response fields, as they can change at any
        time. Send only the fields documented here. Other request fields are not
        supported and their behavior can change.</Warning>
      operationId: add_cmo_message_api_apps__app_id__cmo_messages_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: ID of the app.
            title: App Id
          description: ID of the app.
          example: 6820f3a4e7b91d003c45a1f2
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
                maxLength: 100
                minLength: 1
              - type: 'null'
            description: >-
              Your key for this message, as an alternative to `message_id`. A
              retry with the same key doesn't start a second turn. Ignored when
              the body carries `message_id`.
            title: Idempotency-Key
          description: >-
            Your key for this message, as an alternative to `message_id`. A
            retry with the same key doesn't start a second turn. Ignored when
            the body carries `message_id`.
          example: weekly-plan-2026-09-29
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessagePayload'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketingAgentStateResponse'
        '400':
          description: '`content` is empty.'
        '401':
          description: Missing or invalid credentials.
        '403':
          description: >-
            You don't have editor access to this app or an editor role in its
            workspace, the app is a mobile app, Superagent is turned off for the
            workspace, or you used a read-only or workspace API key.
        '404':
          description: App not found.
        '409':
          description: >-
            The agent is still working on an earlier turn, setup moved on while
            the message was sent, or `message_id` was already used for a
            different message.
        '422':
          description: >-
            The body is malformed, `content` is longer than 20,000 characters,
            `message_id` isn't a UUID, or `Idempotency-Key` is longer than 100
            characters.
        '429':
          description: Rate limit exceeded.
components:
  schemas:
    SendMessagePayload:
      properties:
        message_id:
          type: string
          format: uuid
          title: Message Id
          description: >-
            Your ID for the message. A retry with the same ID doesn't start a
            second turn. Defaults to a new UUID.
          example: 3f2504e0-4f89-11d3-9a0c-0305e82c3301
        content:
          type: string
          maxLength: 20000
          title: Content
          description: The message to the Marketing Agent. Up to 20,000 characters.
          example: Focus on LinkedIn instead of Reddit.
      type: object
      required:
        - content
      title: SendMessagePayload
    MarketingAgentStateResponse:
      properties:
        status:
          type: string
          enum:
            - idle
            - provisioning
            - analyzing
            - ready
            - error
          title: Status
          description: >-
            State of the latest run. Either `"idle"` before any run,
            `"provisioning"` while the workspace's Marketing Agent is created,
            `"analyzing"` while a run is working, `"ready"` once it finished, or
            `"error"` if it failed. A run that stops reporting for 15 minutes
            reads as `"error"`.
          example: ready
        run_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Run Id
          description: >-
            ID of the latest run, or `null` if no run has started. Compare it
            between polls to tell a new run from the one you started.
          example: 68a1c9b7f0b3d9001a7e5d04
        last_error:
          anyOf:
            - type: string
            - type: 'null'
          title: Last Error
          description: Why the latest run failed, or `null` if it didn't.
          example: The scan did not complete. Retry the setup.
        setup_stage:
          type: string
          enum:
            - not_started
            - scanning
            - channels
            - kpis
            - budget
            - generating
            - complete
          title: Setup Stage
          description: >-
            How far setup has got. Either `"not_started"`, `"scanning"`,
            `"channels"`, `"kpis"`, `"budget"`, `"generating"`, or `"complete"`.
            At `channels`, `kpis`, and `budget` the agent is waiting for an
            answer.
          example: channels
        reset_epoch:
          type: integer
          title: Reset Epoch
          description: >-
            Number of times the plan was reset. Send it back as `reset_epoch`
            when you answer a setup step.
          example: 0
        channels:
          items:
            $ref: '#/components/schemas/MarketingChannel'
          type: array
          title: Channels
          description: >-
            The marketing channels the agent proposed. Empty until the opening
            scan finishes.
        channels_revision_suggestion:
          anyOf:
            - type: string
            - type: 'null'
          title: Channels Revision Suggestion
          description: >-
            A change to the channels the agent offers to make, or `null` if it
            offers none. Send it as a message to accept it.
          example: Swap LinkedIn for Product Hunt
        kpis:
          items:
            $ref: '#/components/schemas/MarketingKpi'
          type: array
          title: Kpis
          description: The KPIs the agent proposed. Empty until the opening scan finishes.
        kpis_revision_suggestion:
          anyOf:
            - type: string
            - type: 'null'
          title: Kpis Revision Suggestion
          description: >-
            A change to the KPIs the agent offers to make, or `null` if it
            offers none. Send it as a message to accept it.
          example: Track paid conversions instead of signups
        strategy:
          anyOf:
            - $ref: '#/components/schemas/MarketingStrategy'
            - type: 'null'
          description: The marketing strategy, or `null` until setup writes one.
        suggestions:
          items:
            $ref: '#/components/schemas/MarketingSuggestion'
          type: array
          title: Suggestions
          description: Growth ideas the agent generated for the app.
        budget:
          anyOf:
            - $ref: '#/components/schemas/MarketingBudget'
            - type: 'null'
          description: >-
            The marketing budget given during setup, or `null` if none was given
            yet.
        competitor_research_opt_in:
          type: boolean
          title: Competitor Research Opt In
          description: Whether competitor research runs alongside the strategy.
          example: true
        competitor_research_answer:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Competitor Research Answer
          description: >-
            Whether competitor research was accepted (`true`) or declined
            (`false`), or `null` if the question wasn't answered yet.
          example: true
        setup_paused:
          type: boolean
          title: Setup Paused
          description: >-
            Whether setup is paused because the user asked the agent to stop.
            Sending a message resumes it.
          example: false
        competitors:
          items:
            $ref: '#/components/schemas/MarketingCompetitor'
          type: array
          title: Competitors
          description: Competitors found by the research, with verified URLs.
        competitor_scan_status:
          anyOf:
            - type: string
              enum:
                - running
                - done
                - failed
            - type: 'null'
          title: Competitor Scan Status
          description: >-
            State of the competitor research. Either `"running"`, `"done"`, or
            `"failed"`, or `null` if it never ran.
          example: done
        cmo_agent_app_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Cmo Agent App Id
          description: >-
            ID of the workspace's Marketing Agent app your conversation runs on,
            or `null` if you haven't talked to it on this app yet.
          example: 68a1c2e4f0b3d9001a7e5c21
      type: object
      required:
        - status
        - run_id
        - last_error
        - setup_stage
        - reset_epoch
        - channels
        - channels_revision_suggestion
        - kpis
        - kpis_revision_suggestion
        - strategy
        - suggestions
        - budget
        - competitor_research_opt_in
        - competitor_research_answer
        - setup_paused
        - competitors
        - competitor_scan_status
        - cmo_agent_app_id
      title: MarketingAgentStateResponse
      description: The Marketing Agent's plan for an app and the state of its latest run.
    MarketingChannel:
      properties:
        name:
          type: string
          title: Name
          description: Name of the channel.
          example: Reddit communities
        why:
          type: string
          title: Why
          description: Why this channel fits the app.
          example: Freelancers already trade CRM tips in r/freelance.
        tactics:
          items:
            type: string
          type: array
          title: Tactics
          description: Concrete tactics for the channel. Up to 3.
          example:
            - Share a build-in-public thread
            - Answer lead-tracking questions
        priority:
          anyOf:
            - type: integer
            - type: 'null'
          title: Priority
          description: >-
            How much to invest in this channel, from `1` to `5`, where `5` means
            invest here first. Omitted when the agent didn't rank it.
          example: 5
      type: object
      required:
        - name
        - why
        - tactics
      title: MarketingChannel
    MarketingKpi:
      properties:
        name:
          type: string
          title: Name
          description: The KPI in one measurable line.
          example: Weekly signups
        why:
          type: string
          title: Why
          description: Why this KPI matters for the app.
          example: Signups are the first sign the Reddit threads convert.
        baseline:
          anyOf:
            - type: string
            - type: 'null'
          title: Baseline
          description: >-
            Current value, when the agent could tell it from the app. Omitted
            otherwise.
          example: About 10 a week
        target:
          anyOf:
            - type: string
            - type: 'null'
          title: Target
          description: Target at the recommended budget, with a time horizon.
          example: 50 a week in 90 days
        targets:
          anyOf:
            - additionalProperties:
                type: string
              type: object
            - type: 'null'
          title: Targets
          description: >-
            The target at each budget band, keyed by `none`, `low`, `mid`, and
            `high`. A band that's missing means spend at that level can't move
            this KPI.
          example:
            mid: 50 a week in 90 days
            none: 20 a week in 90 days
        target_values:
          anyOf:
            - additionalProperties:
                type: number
              type: object
            - type: 'null'
          title: Target Values
          description: The number inside each band's target, keyed like `targets`.
          example:
            mid: 50
            none: 20
        metric:
          anyOf:
            - $ref: '#/components/schemas/KpiMetric'
            - type: 'null'
          description: >-
            How the KPI is measured against the app's analytics. Omitted on KPIs
            created before measurement was added.
        instrument:
          anyOf:
            - $ref: '#/components/schemas/KpiInstrument'
            - type: 'null'
          description: >-
            How to start recording the KPI's event. Omitted when the app already
            records it.
      type: object
      required:
        - name
        - why
      title: MarketingKpi
    MarketingStrategy:
      properties:
        summary:
          type: string
          title: Summary
          description: The strategy in up to 2 sentences.
          example: >-
            Win early freelancers in the communities they already use, then turn
            them into referrals.
        positioning:
          anyOf:
            - type: string
            - type: 'null'
          title: Positioning
          description: How the app is positioned against its competitors.
          example: The CRM for freelancers who never wanted a CRM.
        channel_plan:
          items:
            $ref: '#/components/schemas/MarketingChannel'
          type: array
          title: Channel Plan
          description: The channels the strategy invests in.
        key_metrics:
          items:
            type: string
          type: array
          title: Key Metrics
          description: The metrics the strategy is judged by.
          example:
            - Weekly signups
            - Week-one retention
        budget_fit:
          anyOf:
            - type: string
            - type: 'null'
          title: Budget Fit
          description: How the strategy fits the workspace's stated budget.
          example: Organic first, with $100 a month for Reddit promoted posts.
      type: object
      required:
        - summary
        - channel_plan
        - key_metrics
      title: MarketingStrategy
    MarketingSuggestion:
      properties:
        id:
          type: string
          title: Id
          description: ID of the suggestion.
          example: 68a1d0c2f0b3d9001a7e5e10
        title:
          type: string
          title: Title
          description: Short title of the growth idea.
          example: Add a referral link to the dashboard
        builder_prompt:
          type: string
          title: Builder Prompt
          description: Prompt to send to the app builder to build the idea.
          example: >-
            Add a Refer a friend card to the dashboard with a copyable invite
            link.
        category:
          anyOf:
            - type: string
            - type: 'null'
          title: Category
          description: Kind of growth idea.
          example: Referral
        rationale:
          anyOf:
            - type: string
            - type: 'null'
          title: Rationale
          description: Why the idea should help.
          example: Freelancers recommend tools to each other.
        impact:
          anyOf:
            - type: string
            - type: 'null'
          title: Impact
          description: Expected impact. Either `"high"`, `"medium"`, or `"low"`.
          example: high
        effort:
          anyOf:
            - type: string
            - type: 'null'
          title: Effort
          description: Rough build effort. Either `"low"`, `"medium"`, or `"high"`.
          example: low
        dismissed:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Dismissed
          description: Whether someone dismissed the suggestion.
          example: false
      type: object
      required:
        - id
        - title
        - builder_prompt
      title: MarketingSuggestion
    MarketingBudget:
      properties:
        description:
          type: string
          title: Description
          description: The budget as it was stated.
          example: $500 a month
        band:
          anyOf:
            - type: string
              enum:
                - none
                - low
                - mid
                - high
            - type: 'null'
          title: Band
          description: >-
            Budget band that selects each KPI's target. Either `"none"`,
            `"low"`, `"mid"`, or `"high"`. Omitted when the budget was stated in
            free text.
          example: mid
      type: object
      required:
        - description
      title: MarketingBudget
    MarketingCompetitor:
      properties:
        name:
          type: string
          title: Name
          description: Name of the competitor.
          example: HubSpot
        url:
          type: string
          title: Url
          description: URL of the competitor's site.
          example: https://www.hubspot.com
        angle:
          type: string
          title: Angle
          description: How the competitor positions itself.
          example: A full CRM suite for sales teams.
        gap:
          type: string
          title: Gap
          description: What the competitor leaves open for this app.
          example: Too heavy for a solo freelancer.
      type: object
      required:
        - name
        - url
        - angle
        - gap
      title: MarketingCompetitor
    KpiMetric:
      properties:
        template:
          type: string
          enum:
            - event_count
            - unique_sessions
            - unique_users
            - retention_dn
          title: Template
          description: >-
            How the KPI is counted. Either `"event_count"`, `"unique_sessions"`,
            `"unique_users"`, or `"retention_dn"`.
          example: event_count
        event_name:
          type: string
          title: Event Name
          description: Analytics event the KPI is measured against.
          example: lead_created
        window_days:
          anyOf:
            - type: integer
            - type: 'null'
          title: Window Days
          description: Number of days the value is counted over. Either `7` or `30`.
          example: 7
        'n':
          anyOf:
            - type: integer
            - type: 'null'
          title: 'N'
          description: Day the return is measured at, for a `retention_dn` KPI only.
          example: 7
      type: object
      required:
        - template
        - event_name
      title: KpiMetric
    KpiInstrument:
      properties:
        builder_prompt:
          type: string
          title: Builder Prompt
          description: >-
            Prompt for the app builder that adds the missing analytics event.
            Present only while the app doesn't record `metric.event_name` yet.
          example: Track a lead_created event when a lead is saved on the Leads page.
        where:
          anyOf:
            - type: string
            - type: 'null'
          title: Where
          description: >-
            Where in the app's code the outcome already happens. Omitted when
            the agent didn't find it.
          example: src/pages/Leads.jsx:42
        requested_at:
          anyOf:
            - type: string
            - type: 'null'
          title: Requested At
          description: >-
            When the build that adds the event was requested, as an ISO 8601
            timestamp, or `null` if it hasn't been.
          example: '2026-08-24T09:20:00+00:00'
      type: object
      required:
        - builder_prompt
      title: KpiInstrument
  securitySchemes:
    PersonalAccessTokenAuth:
      type: http
      scheme: bearer
      description: 'Personal access token, sent as `Authorization: Bearer <token>`.'

````