ReferenceWhat you can callEventsmutateevents.ingest

events.ingest

Report that something happened outside Selda (a form, an analysis, an ad response). Creates the lead if new, recognises it if known, records it on the timeline, and can put it on a campaign’s review list. Pass autoAdvance to have Selda write the reply from the Brain straight away and leave it in the Sales Inbox, draft.ready is published when it is there. It never sends.

EndpointPOST /mcp/mutate
Scopewrite
Sandbox keyallowed
Extra entitlementinboundIntake
MCP toolsselda_ingest_event
Convex functionmcpQueries:ingestEvent

A free sandbox key may call it.

⚠️
Needs the inboundIntake entitlement on top of the scope. Without it the call comes back 403 and says so, rather than doing half the work.

Arguments

ArgumentTypeRequiredNotes
projectIdstringrequired
typestringrequiredCaller-defined: “form_submitted”, “analysis_completed”, “contract_signed”, anything.
identityobjectrequired
payloadanyoptionalWhatever the event carried. Stored whole; only summarized into the notes line.
sourcestringoptionalWhich system reported it, e.g. “evexcreative.com”. Stored as discoverySource.
occurredAtstring | numberoptionalISO string or epoch ms. An unusable or out-of-range value falls back to now, and says so.
idempotencyKeystringoptionalReplay protection, unique per org. A repeat returns the first result unchanged.
attachToRunIdid<"campaignRuns">optionalPut the lead on this campaign run’s review list as well. The run’s own tone, signature, follow-up rhythm and safety rules already exist and are already approved; an inbound lead joining them beats it inventing a process of its own. It joins at the review stage with no message written, exactly like every other lead on that run.
analysisstringoptionalResearch the caller already did, used by engineV3 as authoritative grounding.
tagsstring[]optional
autoAdvancebooleanoptionalWrite the reply now, from the Brain, and leave it in the Sales Inbox for a person. Off by default because it spends. It never sends: the draft waits for the same approval as every other draft, and draft.ready is published so nobody has to poll to find it. When drafting cannot be done the lead is untouched and draft.failed says why.

Shapes that did not fit the table:

identity: { email?: string; domain?: string; linkedinUrl?: string; name?: string; firstName?: string; lastName?: string; company?: string; jobTitle?: string; phone?: 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/mutate \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "fn": "events.ingest", "args": { "projectId": "<projectId>", "type": "<type>", "identity": {} } }'
{
  "fn": "events.ingest",
  "args": {
    "projectId": "<projectId>",
    "type": "<type>",
    "identity": {}
  }
}

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. mcpQueries:ingestEvent 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.