---
title: "Create a GMV Max automation"
method: POST
path: "/gmv-max/automations"
tags: ["GMV Max"]
---

# Create a GMV Max automation

`POST /gmv-max/automations`

Create a new automation that watches the selected campaign(s) and fires creative boosts when its conditions match. Requires ``read_write`` scope. Idempotent via the ``Idempotency-Key`` header — replays of the same key + same body return the cached 201 instead of creating a duplicate row.

``X-Dry-Run: true`` validates the body and echoes the would-be row without touching the DB. Single shop only — set ``x-shop-id`` to the owning shop.

Every ``set_up_creative_boost`` action requires a ``total_creative_boost_cost`` guardrail (lifetime spend cap) — missing one returns 422 ``TOTAL_BUDGET_CAP_REQUIRED``. Each campaign_id can only belong to one active automation at a time — conflicts return 422 ``CAMPAIGN_ALREADY_ASSIGNED``.

## Headers

- `Idempotency-Key` string, nullable
- `X-Dry-Run` string, nullable

## Request body

- AutomationCreateRequestPublic — ``POST /public/v1/gmv-max/automations`` — create a GMV Max automation. Mirrors ``modules.gmv_max.GmvMaxModels.AutomationCreateRequest`` minus ``shop_id`` (injected from ``x-shop-id``).
  - `name` string, required
  - `template_type` string, nullable — Optional reference to one of the `/templates` `template_type` values the automation was scaffolded from. Free-form — does not affect execution.
  - `trigger_frequency` 'daily' — How often the automation evaluates its conditions. Only ``daily`` is honoured by the offline execution engine today; the portal FE submits this value exclusively.
  - `campaign_ids` string[] — TikTok GMV Max campaign IDs the automation acts on. Each campaign_id can only be assigned to one active automation at a time — the controller rejects conflicts with a 422.
  - `conditions` ConditionRulePublic[]
    - `metric` 'creative_roi' | 'creative_revenue' | 'campaign_roi' | 'campaign_revenue' | 'campaign_cost' | 'click_through_rate' | 'organic_engagement_rate' | 'product_ad_clicks' | '2s_video_view_rate' | 'roi' | 'spend' | 'ctr' | 'spend_pacing', required — Metric a condition evaluates against the campaign / creative window. The first nine values are what the portal FE exposes in the Create-Automation form. The trailing four (``roi``, ``spend``, ``ctr``, ``spend_pacing``) appear in ``/templates`` payloads — accepted so a caller that round-trips a template through ``POST /automations`` is not rejected by validation.
    - `operator` 'lt' | 'gte' | '>=' | '<=' | '>' | '<' | '==', required — Comparison operator. Both the FE's short form (``lt``, ``gte``) and the legacy symbol form (``>=``, ``<``) are accepted — the controller treats them as synonyms.
    - `value` number, required — Threshold the metric is compared against.
    - `lookback_days` integer — How many days of history the metric is computed over. Default 3.
  - `actions` ActionRulePublic[]
    - `action_type` 'set_up_creative_boost', required — Side effect the automation performs when its conditions fire. Only ``set_up_creative_boost`` is wired up to the offline execution engine today. The other portal-side enum values (``budget_adjust``, ``material_status_update``, etc.) are template-preview-only and will not actually execute; they are intentionally not exposed here so callers don't silently create no-op automations.
    - `params` object — Action-specific parameters. For `set_up_creative_boost`: `{"maxCapPerDay": <float>}` is the per-day spend cap (mirrors the portal Create-Automation form).
  - `guardrails` GuardrailRulePublic[]
    - `rule_type` 'total_creative_boost_cost' | 'creative_roi' | 'creative_revenue' | 'campaign_roi' | 'campaign_revenue' | 'campaign_cost' | 'max_budget_daily' | 'cooldown_hours', required — What a guardrail enforces. ``total_creative_boost_cost`` is the lifetime spend cap and is *mandatory* on every ``set_up_creative_boost`` automation (enforced by ``AutomationCreateRequest.validate_total_budget_cap``). The remaining values are metric-name guardrails that cap a specific metric per evaluation window.
    - `operator` 'lt' | 'gte' | '>=' | '<=' | '>' | '<' | '==' — Comparison operator. Both the FE's short form (``lt``, ``gte``) and the legacy symbol form (``>=``, ``<``) are accepted — the controller treats them as synonyms.
    - `value` number, required — Threshold value.
  - `boost_start_date` string, date, nullable — First day boosts may fire. Must be paired with `boost_end_date`. Window cannot exceed 90 days.
  - `boost_end_date` string, date, nullable

## Response `200`

Dry-run echo (no automation created).

## Other responses

- `201` — Automation created.
- `400` — Invalid request.
- `403` — API key lacks `read_write` scope.
- `409` — Idempotency-Key conflict.
- `422` — Validation error (cap missing, campaign conflict, etc.).
- `429` — Write rate limit exceeded.

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) · [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/reacherapp/reacher-data-api/revisions/30786bf76d4b/schema)
