engine.start
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. Pollruns.statusand readawaitingHuman. Nothing is ever sent from here.
| Endpoint | POST /mcp/run |
| Scope | pipeline |
| Sandbox key | refused |
| Extra entitlement | none |
| MCP tools | selda_run_pipeline |
| Convex function | campaignRunner/index:startEngineFromApiKey |
A free sandbox key is refused: it runs the discovery and research engine: web search, crawls, model calls, per-lead credits.
Arguments
| Argument | Type | Required | Notes |
|---|---|---|---|
projectId | id<"projects"> | required | |
idea | string | optional | |
campaignId | id<"campaigns"> | optional | |
targetLeadCount | number | optional | |
startAt | number | optional | |
channels | string[] | optional | 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. |
What Selda fills in
You never send these. The HTTP layer overwrites them from the API key, which is what keeps one organisation’s data out of another’s reach.
| Argument | Filled in from |
|---|---|
orgId | your organisation, resolved from the API key |
apiKeyId | the calling key, so the run records who started it |
Example request
Required arguments only. Values in angle brackets are yours to fill in; the optional ones are in the table above.
curl -X POST https://api.selda.ai/mcp/run \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "fn": "engine.start", "args": { "projectId": "<projectId>" } }'{
"fn": "engine.start",
"args": {
"projectId": "<projectId>"
}
}Example response
Every endpoint answers in the same envelope.
{
"value": <the function's return value>,
"request_id": "req_..."
}The shape of value is not documented here. campaignRunner/index:startEngineFromApiKey declares no returns validator, so there is nothing to derive it from, and a shape written out by hand here is a shape that goes stale without anything noticing. Call it once against a test key and read what comes back.
On failure the body is { "error": { "type", "code", "message", "request_id" }, "request_id" } and request_id is echoed in the X-Request-Id header.
Generated from convex/lib/mcpRegistry.ts. Nothing in this reference sends: launchRun is in no
registry and never will be, so a script can prepare a campaign completely and a person still
presses send in the app.