---
title: "Create Plan"
method: POST
path: "/api/discovery-plans"
tags: ["Discovery"]
---

# Create Plan

`POST /api/discovery-plans`

## Request

- Base URL: `https://api.kolens.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.kolens.ai/api/discovery-plans`
- Auth: HTTP bearer

## Headers

- `x-kolens-user-id` string, nullable

## Request body

- DiscoveryPlanCreate: Issue #163 — the preferred field is now ``platform`` (one of ``tiktok / instagram / x / douyin``). ``data_source`` is retained for backward compat with pre-#163 callers (CLI, Cowork plugin) but emits a deprecation warning server-side and is resolved to a platform via ``discovery_platform_resolver``.
  - `name` string, required
  - `keyword` string, required
  - `platform` string: User-facing platform. One of tiktok / instagram / x / douyin. The backend resolves this to an internal data_source for pipeline dispatch.
  - `interval_hours` integer
  - `max_videos_per_run` integer
  - `region` string, nullable: ISO-3166 alpha-2 country code (e.g. 'US', 'JP', 'SG'). Copied onto every job the scheduler spawns from this plan; the TikTok scrape then browses as if from that country. Omit / empty = the global (US) default.
  - `filters` DiscoveryFilters: Filters applied to scraped KOLs before auto-adding to target_list.
    - `min_followers` integer, nullable
    - `max_followers` integer, nullable
    - `min_avg_views` integer, nullable
    - `min_engagement_rate` number, nullable
    - `require_email` boolean
    - `roles_in` string[], nullable: Only auto-add authors whose dominant role is in this list. Allowed values: KOL, Seller, Customer, Passenger.
  - `target_list_id` string, nullable
  - `status` 'draft' | 'active' | 'disabled': draft (saved, NOT scheduled) | active (scheduler picks up) | disabled (paused). Defaults to draft — creating a plan no longer auto-schedules it; the user opts in by hitting Activate on the detail page once they've verified the keyword + filters.
  - `data_source` string, nullable: DEPRECATED — pass ``platform`` instead. Accepted only for backward compat with pre-#163 callers.

## Response `201`

Successful Response

- DiscoveryPlanOut
  - `id` string, required
  - `name` string, required
  - `keyword` string, required
  - `platform` string, required
  - `interval_hours` integer, required
  - `max_videos_per_run` integer, required
  - `region` string, nullable
  - `filters` object, nullable, required
  - `target_list_id` string, nullable, required
  - `status` 'draft' | 'active' | 'disabled', required
  - `effective_status` 'draft' | 'active' | 'disabled' | 'queued' | 'running' | 'failed', required
  - `data_source` string, required
  - `last_run_at` string, nullable, required
  - `next_run_at` string, nullable, required
  - `last_job_id` string, nullable, required
  - `last_job_status` string, nullable
  - `last_error` string, nullable
  - `created_at` string, required
  - `updated_at` string, required
  - `query_plan` QueryPlanEcho: Display-only echo of how the NL query planner interpreted a typed intent sentence (Phase 1). Returned on the create / search-submit response so the UI can show "Interpreted as: [terms] · filters". Only present when the planner flag is ON *and* the planner produced a real (non-passthrough) interpretation — null otherwise so existing clients ignore it. An editable confirm step is Phase 2.
    - `raw_query` string, required
    - `search_terms` string[]
    - `filters` object
    - `market` string, nullable
    - `language` string, nullable
    - `notes` string

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/kolens/apis/kolens-api.md) · [All operations](https://skmtc.dev/kolens/apis/kolens-api/llms.txt) · [OpenAPI document](https://skmtc.dev/kolens/apis/kolens-api/revisions/5d6865ab0a24?raw)
