---
title: "Submit a project plan"
method: POST
path: "/v2/projects/{id}/submit-plan"
tags: ["Projects"]
---

# Submit a project plan

`POST /v2/projects/{id}/submit-plan`

## Request

- Base URL: `https://api.village.ai`
- URL: `https://api.village.ai/v2/projects/{id}/submit-plan`
- Auth: HTTP bearer

Validate a full project plan through the deterministic gate, persist it onto the project, and start the campaign. Auto-enroll is off by default; when it is turned on for the project, every person whose match_score is 0.8 or above is enrolled into the project’s outreach sequence and emails are drafted to them against the caller’s connected sending account, pausing while 10 drafts await approval and resuming as they clear. No email is sent automatically: each draft waits in the Village app and leaves the mailbox only after the user, or a teammate in their workspace, reads and approves it. No tool here can approve or send an email. Body is the full structured plan. Include `outreach_approach` (the campaign intent gathered from the user) and stamp every sequence step with its canonical `name`, `use_case`, and `audience` — the server materializes canonically named steps from its own template canon, so the stamps matter more than the pasted text. On validation failure returns 422 with the offending `section`, `errors`, and `remediation` — re-draft only that section and resubmit. Part of the project-planning flow — the `plan_a_project` prompt orchestrates create_project → get_planner_skills → get_email_template → submit_plan; call this only after the user has confirmed the plan. Once the campaign reaches the sending stage it needs a connected sending account, which no tool here can add — if the user has none, point them to the Village app: Project -> Outreach -> Sender settings -> Add Email account.

## Path parameters

- `id` string, required: Encrypted project identifier

## Request body

- object
  - `project_type` string, required
  - `brief` string, required
  - `pipeline_stages` object[], required
    - `step` integer, required
    - `name` string, required
    - `description` string, required
    - `target` integer, required
  - `search_prompts` object[], required
    - `prompt` string, required
    - `expected_result_count` integer, required
  - `screening_criteria` object, required
    - `criteria` object[], required
      - `id` string, required
      - `text` string, required
      - `weight` number, nullable, required
  - `sequence_template` object, required
    - `subject` string, required
    - `instructions` string, required
    - `sign_off` string, required: The sign-off the user gave in the planning chat, verbatim, as plain text with line breaks as newlines (e.g. 'Best,\nJane Doe, CEO at Acme'). It must carry a name and no placeholders. The server appends it as the last paragraph of every step, so step bodies end before it.
    - `sender_company` string, nullable: The company the user is reaching out for, as they named it in the chat (e.g. 'Acme'). Fills {sender_company} in every email; the user's profile company is only the fallback. Null when they never named one.
    - `steps` object[], required
      - `template` string, required
      - `name` string, nullable, required
      - `use_case` string, nullable, required
      - `audience` 'warm' | 'cold_referral' | 'cold', nullable: Which journey phase the step belongs to, copied from the canonical template's stamp: 'warm' greets a mutual contact asking for an intro, 'cold_referral' greets the target naming the mutual, 'cold' greets the target with no mutual. Omitting it forces the runtime to guess from the greeting.
  - `sequence_name` string, required
  - `sequence_description` string, required
  - `outreach_approach` 'warm_intro' | 'cold_warm_lever' | 'cold': The campaign intent gathered from the user: 'warm_intro' asks mutual contacts for introductions first, 'cold_warm_lever' emails targets directly naming a mutual when one exists, 'cold' emails targets directly with no mutual mentioned. Sets each newly enrolled lead's default path (overridable per lead in the app). Copy ALL template sets into steps regardless — the approach picks the default, not which sets exist.

## Response `200`

Submission outcome

- object
  - `data` SubmitPlanResponse, required: Outcome of submitting a project plan through the deterministic gate.
    - `status` 'submitted' | 'invalid_plan' | 'error', required
    - `message` string, required
    - `section` string
    - `errors` string[]
    - `remediation` string
  - `metadata` object, required
    - `request_id` string, required

## Other responses

- `400`: Bad request - invalid parameters
- `401`: Unauthorized - missing or invalid authentication
- `404`: Project not found
- `422`: Plan failed validation

---

[API](https://skmtc.dev/village/apis/village-v2-api.md) - [All operations](https://skmtc.dev/village/apis/village-v2-api/llms.txt) - [OpenAPI document](https://skmtc.dev/village/apis/village-v2-api/revisions/aebe21a1a544?raw)
