ReferenceWhat you can callMaterial (files)runmaterial.import

material.import

Your prospect folder → a campaign + company list, then it stops. Uploading material is not permission to send.

EndpointPOST /mcp/run
Scopepipeline
Sandbox keyallowed
Extra entitlementnone
MCP toolsselda_import_material
Convex functioncampaignRunner/folderImportApi:importProspectMaterial

A free sandbox key may call it, but not with every argument. See the note below.

Whether a sandbox key gets through depends on the arguments. Importing your own material costs Selda nothing, so it is free; autoAdvance carries the run on into contact lookup and drafting, which spend, so with it the same call needs a live key.

Arguments

ArgumentTypeRequiredNotes
projectIdid<"projects">required
filesobject[]required
assignments{ folderName: string; domain: string }[]optional
excludedFoldersstring[]optional
campaignNamestringoptional
targetRunIdid<"campaignRuns">optionalPush INTO an existing campaign instead of creating one (#357), the campaign’s run id. The other door onto the same feature: a script that already produced a second folder of prospects for the same motion should be able to add them as the campaign’s next wave rather than fragmenting one effort across several campaigns. The permission rule is unchanged, the wave parks at the company-list gate and inherits nothing from an earlier wave’s grant, and the run must belong to the project this API key’s org owns, which appendCandidateBatch verifies.
campaignBriefstringoptionalThe operator’s campaign document (18.8.2026): rules, structure, tone, what may not be claimed. This is what made the API pipeline half-manual, analyses went in over the wire, but the RULES had to be pasted by hand in the app, and a campaign without them runs on house defaults. Honored exactly as the Määritä card’s own-prompt mode is. On a targetRunId wave it is accepted only when the campaign has no brief yet; a differing one is refused out loud.
autoAdvanceboolean | ("leads" | "messages")[]optionalExplicit grant to continue past the company list. A script CAN pass this, Autopilot is a legitimate caller, but it is never the default, and the grant is recorded on the run. Per-stage since #301: ["leads"] grants contact lookup and stops before any message is written; true grants both. A script that only wants the contacts found should say so rather than granting everything, because message writing spends credits. Neither form can send. Sending stays behind launchRun and the approval gate.

Shapes that did not fit the table:

files: { path: string; storageId: id<"_storage">; sizeBytes: number; mimeType?: string }[]

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.

ArgumentFilled in from
orgIdyour organisation, resolved from the API key

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": "material.import", "args": { "projectId": "<projectId>", "files": [{}] } }'
{
  "fn": "material.import",
  "args": {
    "projectId": "<projectId>",
    "files": [{}]
  }
}

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/folderImportApi:importProspectMaterial 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.