---
title: "engine.start"
method: POST
path: "/v1/engine/start"
tags: ["engine"]
---

# engine.start

`POST /v1/engine/start`

## Request

- Base URL: `https://api.selda.ai`
- URL: `https://api.selda.ai/v1/engine/start`
- Auth: HTTP bearer

The full pipeline from a brief: find companies → research → fit → hook → draft. It STOPS at the company list (run status `awaiting_profile`) and waits for a person to confirm the companies and the decision-maker roles in the Selda app. Poll `runs.status` and read `awaitingHuman`. Nothing is ever sent from here.

Dispatched to `engine.start` on `POST /mcp/run`. This route is a different URL for the same call: the request is answered by the same dispatcher, with the same authentication, the same scope check, the same organisation scoping and the same refusals.

- Scope: `pipeline`
- A free sandbox key is refused: it runs the discovery and research engine: web search, crawls, model calls, per-lead credits
- MCP tools that wrap the same function: `selda_run_pipeline`
- Filled in from your API key, never sent by you: `orgId`, `apiKeyId`

## Request body

- object
  - `projectId` string, required: A Convex document id in the `projects` table.
  - `idea` string
  - `campaignId` string: A Convex document id in the `campaigns` table.
  - `targetLeadCount` number
  - `startAt` number
  - `channels` string[]: WHICH CHANNELS THIS CAMPAIGN SENDS ON, the API's version of the Määritä card's Kanavat step. Without it this door could not name a channel at all: it auto-creates a campaign, and `createCampaignForEngine` then stamps `["email"]`, so every LinkedIn campaign an agent or an integration started was an email campaign and nothing said so. It is read by discovery, by the contact ladder, by the writer and by `launchRun`, so a wrong value here is not cosmetic. Applies to the campaign THIS call creates. When `campaignId` names an existing campaign that campaign's own `channels` govern (change them with `campaigns.update`), the run must not be able to disagree with the campaign it belongs to. Absent = unchanged: the campaign's default.

## Response `200`

The call succeeded.

- object
  - `value` unknown, required
  - `request_id` string, required: Echoed in the `X-Request-Id` header.

## Other responses

- `400`: Unknown route, or arguments the validator refused.
- `401`: Missing, expired or revoked key.
- `403`: The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.
- `429`: Rate limited. Wait for the next minute window and retry.
- `500`: The function threw. The message says what, without leaking internals.

---

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