---
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`

<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, nullable — 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
      - `builder_prompt` string, required — Prompt for the app builder that adds 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.
    - `builder_prompt` string, required — Prompt to send to the app builder 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.
- `404` — App not found.
- `422` — `Idempotency-Key` is longer than 100 characters.
- `429` — Rate limit exceeded.

## Changes

> 22 revisions in range; 1 not diffed.

- **2026-09-30** `cbaf216bfb03` — 1 info
  - endpoint added

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

---

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