---
title: "Start Marketing Agent setup"
method: POST
path: "/api/apps/{app_id}/cmo/setup/start"
---

# Start Marketing Agent setup

`POST /api/apps/{app_id}/cmo/setup/start`

## Request

- Base URL: `https://app.base44.com`
- URL: `https://app.base44.com/api/apps/{app_id}/cmo/setup/start`
- Auth: HTTP bearer

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

Starts the Marketing Agent's setup for an app. The agent reads the app and drafts marketing channels and KPIs for you to review.

The first run in a workspace also creates the workspace's Marketing Agent, a Superagent that everyone in the workspace shares.

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`. Setup then waits at a `setup_stage` of `channels`. Approve each step with [Answer Marketing Agent setup step](/api-reference/answer-marketing-agent-setup-step), or ask for changes with [Send Marketing Agent message](/api-reference/send-marketing-agent-message).

While the scan is running, or once setup is past it, the call returns the current state and starts nothing. After a failed scan it starts the scan again, which is a new charged run.

To retry safely, send an `Idempotency-Key` header. A retry with the same key within 24 hours returns the current state and starts nothing, even if the first scan failed.

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.</Warning>

## Path parameters

- `app_id` string, required: ID of the app.

## Headers

- `Idempotency-Key` string: Your key for this call. A retry with the same key within 24 hours returns the current state and starts nothing.

## Response `200`

Successful Response

- MarketingAgentStateResponse: The Marketing Agent's plan for an app and the state of its latest run.
  - `status` 'idle' | 'provisioning' | 'analyzing' | 'ready' | 'error', required: 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"`.
  - `run_id` string, nullable, required: 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.
  - `last_error` string, nullable, required: Why the latest run failed, or `null` if it didn't.
  - `setup_stage` 'not_started' | 'scanning' | 'channels' | 'kpis' | 'budget' | 'generating' | 'complete', required: 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.
  - `reset_epoch` integer, required: Number of times the plan was reset. Send it back as `reset_epoch` when you answer a setup step.
  - `channels` MarketingChannel[], required: The marketing channels the agent proposed. Empty until the opening scan finishes.
    - `name` string, required: Name of the channel.
    - `why` string, required: Why this channel fits the app.
    - `tactics` string[], required: Concrete tactics for the channel. Up to 3.
    - `priority` integer, nullable: 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.
  - `channels_revision_suggestion` string, nullable, required: A change to the channels the agent offers to make, or `null` if it offers none. Send it as a message to accept it.
  - `kpis` MarketingKpi[], required: The KPIs the agent proposed. Empty until the opening scan finishes.
    - `name` string, required: The KPI in one measurable line.
    - `why` string, required: Why this KPI matters for the app.
    - `baseline` string, nullable: Current value, when the agent could tell it from the app. Omitted otherwise.
    - `target` string, nullable: Target at the recommended budget, with a time horizon.
    - `targets` object, nullable: 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.
    - `target_values` object, nullable: The number inside each band's target, keyed like `targets`.
    - `metric` KpiMetric
      - `template` 'event_count' | 'unique_sessions' | 'unique_users' | 'retention_dn', required: How the KPI is counted. Either `"event_count"`, `"unique_sessions"`, `"unique_users"`, or `"retention_dn"`.
      - `event_name` string, required: Analytics event the KPI is measured against.
      - `window_days` integer, nullable: Number of days the value is counted over. Either `7` or `30`.
      - `n` integer, nullable: Day the return is measured at, for a `retention_dn` KPI only.
    - `instrument` KpiInstrument
      - `chat_prompt` string, required: Prompt to send to the app's AI chat to add the missing analytics event. Present only while the app doesn't record `metric.event_name` yet.
      - `where` string, nullable: Where in the app's code the outcome already happens. Omitted when the agent didn't find it.
      - `requested_at` string, nullable: When the build that adds the event was requested, as an ISO 8601 timestamp, or `null` if it hasn't been.
  - `kpis_revision_suggestion` string, nullable, required: A change to the KPIs the agent offers to make, or `null` if it offers none. Send it as a message to accept it.
  - `strategy` MarketingStrategy, required
    - `summary` string, required: The strategy in up to 2 sentences.
    - `positioning` string, nullable: How the app is positioned against its competitors.
    - `channel_plan` MarketingChannel[], required: The channels the strategy invests in.
      - `name` string, required: Name of the channel.
      - `why` string, required: Why this channel fits the app.
      - `tactics` string[], required: Concrete tactics for the channel. Up to 3.
      - `priority` integer, nullable: 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.
    - `key_metrics` string[], required: The metrics the strategy is judged by.
    - `budget_fit` string, nullable: How the strategy fits the workspace's stated budget.
  - `suggestions` MarketingSuggestion[], required: Growth ideas the agent generated for the app.
    - `id` string, required: ID of the suggestion.
    - `title` string, required: Short title of the growth idea.
    - `chat_prompt` string, required: Prompt to send to the app's AI chat to build the idea.
    - `category` string, nullable: Kind of growth idea.
    - `rationale` string, nullable: Why the idea should help.
    - `impact` string, nullable: Expected impact. Either `"high"`, `"medium"`, or `"low"`.
    - `effort` string, nullable: Rough build effort. Either `"low"`, `"medium"`, or `"high"`.
    - `dismissed` boolean, nullable: Whether someone dismissed the suggestion.
  - `budget` MarketingBudget, required
    - `description` string, required: The budget as it was stated.
    - `band` 'none' | 'low' | 'mid' | 'high', nullable: Budget band that selects each KPI's target. Either `"none"`, `"low"`, `"mid"`, or `"high"`. Omitted when the budget was stated in free text.
  - `competitor_research_opt_in` boolean, required: Whether competitor research runs alongside the strategy.
  - `competitor_research_answer` boolean, nullable, required: Whether competitor research was accepted (`true`) or declined (`false`), or `null` if the question wasn't answered yet.
  - `setup_paused` boolean, required: Whether setup is paused because the user asked the agent to stop. Sending a message resumes it.
  - `competitors` MarketingCompetitor[], required: Competitors found by the research, with verified URLs.
    - `name` string, required: Name of the competitor.
    - `url` string, required: URL of the competitor's site.
    - `angle` string, required: How the competitor positions itself.
    - `gap` string, required: What the competitor leaves open for this app.
  - `competitor_scan_status` 'running' | 'done' | 'failed', nullable, required: State of the competitor research. Either `"running"`, `"done"`, or `"failed"`, or `null` if it never ran.
  - `cmo_agent_app_id` string, nullable, required: 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.

## Other responses

- `401`: Missing or invalid credentials.
- `403`: 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. Setup, chat, generation and enabling autonomy also return `app_marketing_not_allowed` when the app's owning workspace has the FERPA designation. Historical reads, reset and disabling autonomy remain available.
- `404`: App not found.
- `422`: `Idempotency-Key` is longer than 100 characters.
- `429`: Rate limit exceeded.

## Changes

- **2026-10-21** `c23775445de2`: 2 breaking, 1 warning, 1 info
  - `header` request parameter `Idempotency-Key` list-of-types was narrowed by removing types `null`
  - for the `header` request parameter `Idempotency-Key`, the minLength was increased from `0` to `1`
  - for the `header` request parameter `Idempotency-Key`, the maxLength was set to `100`
  - for the `header` request parameter `Idempotency-Key`, the type was generalized from no type to `string`
- **2026-10-19** `afddf0835104`: 2 breaking, 2 info
  - removed the required property `kpis/items/instrument/anyOf[subschema #1: KpiInstrument]/builder_prompt` from the response with the `200` status
  - removed the required property `suggestions/items/builder_prompt` from the response with the `200` status
  - added the required property `kpis/items/instrument/anyOf[subschema #1: KpiInstrument]/chat_prompt` to the response with the `200` status
  - added the required property `suggestions/items/chat_prompt` to the response with the `200` status
- **2026-10-11** `80287813ab84`: 1 info
  - the endpoint scheme security `PersonalAccessTokenAuth` was added to the API
- **2026-09-30** `e2a6a9f1fe4c`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/base44/apis/base44-app-management-api/changes/api/apps/:app_id/cmo/setup/start/post.md)

---

[API](https://skmtc.dev/base44/apis/base44-app-management-api.md) - [All operations](https://skmtc.dev/base44/apis/base44-app-management-api/llms.txt) - [OpenAPI document](https://skmtc.dev/base44/apis/base44-app-management-api/revisions/c534f09cf314?raw)
