> ## 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.

# Answer Marketing Agent setup step

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

Answers the setup step the Marketing Agent is waiting on with a fixed choice, without an agent turn.

Send the state's `setup_stage` as `stage`, along with its `reset_epoch`. Each step takes its own answers:
- `channels` takes `channels_ok`, which approves the channels and moves setup to `kpis`.
- `kpis` takes `kpis_ok`, which approves the KPIs and moves setup to `budget`.
- `budget` takes `budget` and `competitor_research`, together or one at a time. Once both are known, setup moves to `generating` and the agent writes the strategy. Send `budget_go` on its own to confirm a pair that's already stored.

To ask for a change instead, use [Send Marketing Agent message](/api-reference/send-marketing-agent-message).

Most answers are free and take effect at once. The one that completes the budget step starts the strategy run. 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`.

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.

To retry safely, send the same `message_id` or `Idempotency-Key` header again. A retry returns the current state and applies nothing, so it never starts a second strategy run.

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/setup/answer
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/setup/answer:
    post:
      summary: Answer Marketing Agent setup step
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Answers the setup step the Marketing Agent is waiting on with a fixed
        choice, without an agent turn.


        Send the state's `setup_stage` as `stage`, along with its `reset_epoch`.
        Each step takes its own answers:

        - `channels` takes `channels_ok`, which approves the channels and moves
        setup to `kpis`.

        - `kpis` takes `kpis_ok`, which approves the KPIs and moves setup to
        `budget`.

        - `budget` takes `budget` and `competitor_research`, together or one at
        a time. Once both are known, setup moves to `generating` and the agent
        writes the strategy. Send `budget_go` on its own to confirm a pair
        that's already stored.


        To ask for a change instead, use [Send Marketing Agent
        message](/api-reference/send-marketing-agent-message).


        Most answers are free and take effect at once. The one that completes
        the budget step starts the strategy run. 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`.


        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.


        To retry safely, send the same `message_id` or `Idempotency-Key` header
        again. A retry returns the current state and applies nothing, so it
        never starts a second strategy run.


        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: answer_cmo_setup_step_api_apps__app_id__cmo_setup_answer_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 answer, as an alternative to `message_id`. A
              retry with the same key returns the current state and applies
              nothing. Ignored when the body carries `message_id`.
            title: Idempotency-Key
          description: >-
            Your key for this answer, as an alternative to `message_id`. A retry
            with the same key returns the current state and applies nothing.
            Ignored when the body carries `message_id`.
          example: weekly-plan-2026-09-29
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetupAnswerPayload'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketingAgentStateResponse'
        '400':
          description: The answers don't match the questions `stage` asks.
        '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, or `stage` isn't `channels`, `kpis`, or `budget`.
        '409':
          description: >-
            Setup isn't on `stage` any more, the plan was reset since
            `reset_epoch`, the agent is still working, `budget_go` was sent
            before both budget answers were stored, or `message_id` was already
            used for a different message.
        '422':
          description: >-
            The body is malformed, the `budget` answer has an empty
            `selected_label`, the `competitor_research` answer has no `value`,
            or `Idempotency-Key` is longer than 100 characters.
        '429':
          description: Rate limit exceeded.
components:
  schemas:
    SetupAnswerPayload:
      properties:
        stage:
          type: string
          maxLength: 40
          title: Stage
          description: >-
            The step you're answering, from the state's `setup_stage`. Either
            `"channels"`, `"kpis"`, or `"budget"`.
          example: budget
        reset_epoch:
          anyOf:
            - type: integer
              minimum: 0
            - type: 'null'
          title: Reset Epoch
          description: >-
            The state's `reset_epoch`, so the answer is rejected if the plan was
            reset since you read it. Omit it to skip that check.
          example: 0
        answers:
          items:
            $ref: '#/components/schemas/SetupAnswerItem'
          type: array
          maxItems: 4
          minItems: 1
          title: Answers
          description: Answers to the step's questions. Between 1 and 4.
        message_id:
          type: string
          format: uuid
          title: Message Id
          description: >-
            ID for the chat message that records the answer. Use a new UUID for
            every answer, and the same one when you retry it. A retry with the
            same ID returns the current state and applies nothing. Defaults to a
            new UUID.
          example: 3f2504e0-4f89-11d3-9a0c-0305e82c3301
      type: object
      required:
        - stage
        - answers
      title: SetupAnswerPayload
    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.
    SetupAnswerItem:
      properties:
        key:
          type: string
          maxLength: 40
          title: Key
          description: >-
            Question being answered. Either `"channels_ok"`, `"kpis_ok"`,
            `"budget"`, `"competitor_research"`, or `"budget_go"`.
          example: budget
        selected_label:
          type: string
          maxLength: 200
          minLength: 1
          title: Selected Label
          description: >-
            The answer as the user would say it. It's recorded as their message
            in the app's chat, and for `budget` it's the stored budget.
          example: $500 a month
        value:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Value
          description: >-
            For `competitor_research`, whether to research competitors (`true`)
            or not (`false`). Ignored for other questions.
          example: true
        band:
          anyOf:
            - type: string
              maxLength: 20
            - type: 'null'
          title: Band
          description: >-
            For `budget`, the budget band that selects each KPI's target. Either
            `"none"`, `"low"`, `"mid"`, or `"high"`. Any other value is ignored.
          example: mid
      type: object
      required:
        - key
        - selected_label
      title: SetupAnswerItem
    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>`.'

````