{
  "openapi": "3.1.0",
  "info": {
    "title": "Selda API",
    "version": "1.0.0",
    "summary": "Find the right people, write the message, prepare the campaign. A person still presses send.",
    "description": "Generated from `convex/lib/mcpRegistry.ts`, the same table the endpoints dispatch from. A\nfunction added to the registry appears here on the next build and one removed disappears, so\nthis document cannot describe a capability the API does not have.\n\n## Authentication\n\n`Authorization: Bearer sk_live_...` or `Bearer sk_test_...`. **Your organisation is resolved\nfrom the key.** You never send an `orgId`, and a key cannot reach a workspace outside the\norganisation it was made in. A `sk_test_` key reaches every read and write that costs Selda\nnothing and cannot spend or send; each operation says whether it refuses one, and why.\n\n## These paths and the RPC endpoints are the same API\n\nEvery route here is dispatched by the same code that answers `POST /mcp/query`,\n`POST /mcp/mutate` and `POST /mcp/run`. The path chooses the function name; nothing else\nchanges. The RPC form is not deprecated and is still the only way to call the few entries\nthat have no route.\n\n## What is not here, on purpose\n\n**There is no operation that sends.** `launchRun` is in no registry, not this one and not the\nMCP one, and it never will be. A script can prepare a campaign completely: import material,\nconfirm the companies, write and rewrite every draft. A human presses send in the app. That is\nthe product's central claim, not a gap waiting to be filled.\n\n**There is no operation that creates a workspace.** `projects.create` is absent rather than\ngated, because a rule enforced by a plan tier is a rule with a price on it.\n\n**Response shapes are almost never documented.** A shape for `value` is published for\n1 of the 71 operations, because one is derived only where the target function declares\na `returns` validator. Everywhere else `value` carries a description saying exactly that,\nrather than an invented object that would go stale with nothing to notice. Call an operation\nonce with a test key and read what comes back.\n\n## Registry entries with no route\n\n- `leads.enrich` on `POST /mcp/run`: another registry table (INTERNAL_ACTIONS) declares leads.enrich on /mcp/run too and the dispatcher checks that one first, so this entry can never be the one that runs.\n- `leads.update` on `POST /mcp/mutate`: another registry table (INTERNAL_MUTATIONS) declares leads.update on /mcp/mutate too and the dispatcher checks that one first, so this entry can never be the one that runs.\n- `replies.draft` on `POST /mcp/run`: `POST /v1/replies/draft` is already the route for `replies.draft` on `/mcp/mutate`, and one URL cannot mean two functions. Call this one at `POST /mcp/run` with `{ \"fn\": \"replies.draft\" }`."
  },
  "servers": [
    {
      "url": "https://api.selda.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "brain",
      "description": "The structured knowledge a human put in: products, partners, references, company facts, and the things Selda must never say."
    },
    {
      "name": "campaigns",
      "description": "Campaigns and the message structure that governs what their messages are made of."
    },
    {
      "name": "company",
      "description": "Resolving a company and the people to reach in it."
    },
    {
      "name": "connectors",
      "description": "Data connectors registered on a workspace."
    },
    {
      "name": "credits",
      "description": "Credit balance, usage and plan."
    },
    {
      "name": "drafts",
      "description": "The drafted message on one run lead. Editing a draft never sends it."
    },
    {
      "name": "engine",
      "description": "The full pipeline from a brief. It stops at the company list and waits for a person."
    },
    {
      "name": "events",
      "description": "Reporting that something happened outside Selda."
    },
    {
      "name": "flows",
      "description": "What a workspace does with something arriving from outside."
    },
    {
      "name": "inbox",
      "description": "The Sales Inbox: putting a message into a conversation, in either direction."
    },
    {
      "name": "knowledge",
      "description": "The prose that grounds every message."
    },
    {
      "name": "leads",
      "description": "Companies and people in a workspace."
    },
    {
      "name": "material",
      "description": "Prospect material: raw files in, a campaign and a company list out."
    },
    {
      "name": "messages",
      "description": "Messages in a workspace, and the drafting of new ones. Nothing here sends."
    },
    {
      "name": "projects",
      "description": "Workspaces. A workspace is created in the Selda app, by a person, never over the API."
    },
    {
      "name": "replies",
      "description": "Answering an inbound message. Every one of these writes a draft a person still sends."
    },
    {
      "name": "runs",
      "description": "Campaign runs: their status, their companies, their drafts."
    },
    {
      "name": "webhooks",
      "description": "Outbound webhook endpoints, registered per organisation."
    }
  ],
  "paths": {
    "/v1/brain": {
      "get": {
        "operationId": "brain_list",
        "summary": "brain.list",
        "description": "The workspace's structured knowledge: products, partners, references, company facts, and the things Selda must never say. Each item has a type, a title and a body.\n\nDispatched to `brain.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_brain`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "brain"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `projects` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listBrainItems` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "brain.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_brain"
        ]
      },
      "post": {
        "operationId": "brain_add",
        "summary": "brain.add",
        "description": "Add one thing Selda should know: a product, a partner, a reference, a company fact, a note, something it must never say, or a `writing_rule` — a standing instruction about HOW messages are written, which reaches the composer as a directive and is never quoted as material.\n\nDispatched to `brain.add` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_add_brain_item`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "brain"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addBrainItemInternal` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "brain.add",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_add_brain_item"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "company",
                      "product",
                      "partner",
                      "reference",
                      "avoid",
                      "note",
                      "writing_rule"
                    ]
                  },
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "type",
                  "title",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/brain/remove": {
      "post": {
        "operationId": "brain_remove",
        "summary": "brain.remove",
        "description": "Take one Brain item back out. The human owns what Selda knows.\n\nDispatched to `brain.remove` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_remove_brain_item`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "brain"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:removeBrainItemInternal` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "brain.remove",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_remove_brain_item"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "id": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "id"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/brain/update": {
      "post": {
        "operationId": "brain_update",
        "summary": "brain.update",
        "description": "Rewrite the title and body of one Brain item.\n\nDispatched to `brain.update` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_update_brain_item`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "brain"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:updateBrainItemInternal` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "brain.update",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_update_brain_item"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "id": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "id"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/campaigns": {
      "get": {
        "operationId": "campaigns_list",
        "summary": "campaigns.list",
        "description": "Campaigns in a workspace.\n\nDispatched to `campaigns.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_campaigns`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listCampaigns` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_campaigns"
        ]
      },
      "post": {
        "operationId": "campaigns_create",
        "summary": "campaigns.create",
        "description": "Create a campaign (legacy table, not the one the app's campaign-flow UI reads).\n\nDispatched to `campaigns.create` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`, `userId`",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:createCampaign` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.create",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "brief": {
                    "type": "string"
                  },
                  "outreachAngle": {
                    "type": "string"
                  },
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "targetRole": {
                    "type": "string"
                  },
                  "targetIndustry": {
                    "type": "string"
                  },
                  "targetCompanySize": {
                    "type": "string"
                  },
                  "locations": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "leadCount": {
                    "type": "number"
                  },
                  "startAt": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "startedAt": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "scheduledStartDate": {
                    "type": "number"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{campaignId}": {
      "get": {
        "operationId": "campaigns_get",
        "summary": "campaigns.get",
        "description": "One campaign: status, channels, settings, leads.\n\nDispatched to `campaigns.get` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_campaign`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getCampaign` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.get",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_campaign"
        ]
      },
      "patch": {
        "operationId": "campaigns_update",
        "summary": "campaigns.update",
        "description": "Change a campaign.\n\nDispatched to `campaigns.update` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:updateCampaign` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.update",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "goal": {
                    "type": "string"
                  },
                  "brief": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "ghostwriterHints": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "outreachAngle": {
                    "type": "string"
                  },
                  "scheduledStartDate": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{campaignId}/add-leads": {
      "post": {
        "operationId": "campaigns_addLeads",
        "summary": "campaigns.addLeads",
        "description": "Put specific leads into a campaign.\n\nDispatched to `campaigns.addLeads` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLeadsToCampaign` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.addLeads",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "A Convex document id in the `leads` table."
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadIds"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{campaignId}/lock-message-structure": {
      "post": {
        "operationId": "campaigns_lockMessageStructure",
        "summary": "campaigns.lockMessageStructure",
        "description": "Lock a campaign's message structure so every locked block ships exactly as written and nothing rewrites it, or unlock it with locked: false. Sends nothing.\n\nDispatched to `campaigns.lockMessageStructure` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_lock_message_structure`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignBlueprints:setMessageStructureLockForApi` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.lockMessageStructure",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_lock_message_structure"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "locked": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "locked"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{campaignId}/message-structure": {
      "get": {
        "operationId": "campaigns_messageStructure",
        "summary": "campaigns.messageStructure",
        "description": "Read what a campaign's message is made of: every block, which ones ship verbatim, the instruction behind each generated one, the shape, and whether it is locked.\n\nDispatched to `campaigns.messageStructure` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_message_structure`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "anyOf": [
                        {
                          "description": "Any JSON value. The validator is `v.any()`."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.messageStructure",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_message_structure"
        ]
      }
    },
    "/v1/campaigns/{campaignId}/set-message-structure": {
      "post": {
        "operationId": "campaigns_setMessageStructure",
        "summary": "campaigns.setMessageStructure",
        "description": "State what a campaign's message is made of: blocks that ship WORD FOR WORD, blocks Selda writes from an instruction you give it, the paragraph count, and what must never appear. Refuses to change a locked structure. Sends nothing.\n\nDispatched to `campaigns.setMessageStructure` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_set_message_structure`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignBlueprints:setMessageStructureForApi` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.setMessageStructure",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_set_message_structure"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "objective": {
                    "type": "string"
                  },
                  "blocks": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "order": {
                          "type": "number"
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "subject",
                            "body"
                          ]
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "locked",
                            "generated"
                          ]
                        },
                        "label": {
                          "type": "string"
                        },
                        "content": {
                          "type": "string"
                        },
                        "aiInstruction": {
                          "type": "string"
                        },
                        "optional": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "kind"
                      ]
                    }
                  },
                  "form": {
                    "type": "object",
                    "properties": {
                      "paragraphs": {
                        "type": "number"
                      },
                      "maxWords": {
                        "type": "number"
                      },
                      "links": {
                        "type": "string",
                        "enum": [
                          "none",
                          "allowed"
                        ]
                      }
                    }
                  },
                  "mustNever": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "must": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "followUpStrategy": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{campaignId}/stats": {
      "get": {
        "operationId": "campaigns_stats",
        "summary": "campaigns.stats",
        "description": "Campaign counters: sent, delivered, opened, clicked, replied, bounced.\n\nDispatched to `campaigns.stats` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_campaign_stats`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getCampaignStats` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.stats",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_campaign_stats"
        ]
      }
    },
    "/v1/campaigns/add-leads-by-tag": {
      "post": {
        "operationId": "campaigns_addLeadsByTag",
        "summary": "campaigns.addLeadsByTag",
        "description": "Put every lead carrying a tag into a campaign (legacy table).\n\nDispatched to `campaigns.addLeadsByTag` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLeadsToCampaignByTag` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.addLeadsByTag",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "campaignId": {
                    "type": "string"
                  },
                  "projectId": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "campaignId",
                  "projectId",
                  "tags"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/add-rule": {
      "post": {
        "operationId": "campaigns_addRule",
        "summary": "campaigns.addRule",
        "description": "Add a campaign rule.\n\nDispatched to `campaigns.addRule` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "campaigns"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addCampaignRule` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "campaigns.addRule",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "campaignId": {
                    "type": "string",
                    "description": "A Convex document id in the `campaigns` table."
                  },
                  "condition": {
                    "type": "object",
                    "properties": {
                      "tag": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "tag"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "campaignId",
                  "condition"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/company/lookup": {
      "post": {
        "operationId": "company_lookup",
        "summary": "company.lookup",
        "description": "Resolve a company and return the right people to reach. Starts nothing.\n\nDispatched to `company.lookup` 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it resolves real people's contact details through a paid provider, which costs money per call\n- MCP tools that wrap the same function: `selda_lookup`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "company"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:lookupCompany` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "company.lookup",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_contact_data",
        "x-selda-mcp-tools": [
          "selda_lookup"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "company": {
                    "type": "string"
                  },
                  "role": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "number"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "company"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/connectors": {
      "get": {
        "operationId": "connectors_list",
        "summary": "connectors.list",
        "description": "Data connectors registered for this workspace.\n\nDispatched to `connectors.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_connectors`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "connectors"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `projects` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `dataConnectors:listConnectors` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "connectors.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_connectors"
        ]
      },
      "post": {
        "operationId": "connectors_create",
        "summary": "connectors.create",
        "description": "Register a data connector.\n\nDispatched to `connectors.create` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "connectors"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `dataConnectors:createConnector` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "connectors.create",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "name": {
                    "type": "string"
                  },
                  "logoUrl": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "authHeader": {
                    "type": "string"
                  },
                  "fieldMap": {
                    "type": "object",
                    "properties": {
                      "company": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string"
                      },
                      "externalUrl": {
                        "type": "string"
                      },
                      "companyDomain": {
                        "type": "string"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "notes": {
                        "type": "string"
                      }
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "name",
                  "url",
                  "fieldMap"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/connectors/{connectorId}": {
      "delete": {
        "operationId": "connectors_delete",
        "summary": "connectors.delete",
        "description": "Remove a data connector.\n\nDispatched to `connectors.delete` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "connectors"
        ],
        "parameters": [
          {
            "name": "connectorId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `dataConnectors` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `dataConnectors:deleteConnector` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "connectors.delete",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed"
      }
    },
    "/v1/connectors/{connectorId}/sync": {
      "post": {
        "operationId": "connectors_sync",
        "summary": "connectors.sync",
        "description": "Pull from a connected data source.\n\nDispatched to `connectors.sync` 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it pulls from a connected outside source and enriches as it goes\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "connectors"
        ],
        "parameters": [
          {
            "name": "connectorId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `dataConnectors` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `dataConnectors:syncConnector` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "connectors.sync",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "external_sync"
      }
    },
    "/v1/credits/info": {
      "get": {
        "operationId": "credits_info",
        "summary": "credits.info",
        "description": "Credit balance, daily free credits, usage, plan.\n\nDispatched to `credits.info` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_credits`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "credits"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getCreditInfo` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "credits.info",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_credits"
        ]
      }
    },
    "/v1/drafts/remove": {
      "post": {
        "operationId": "drafts_remove",
        "summary": "drafts.remove",
        "description": "Take one draft out of a run so it cannot be sent. The row stays visible with your reason and the app can put it back. Refuses a message that already went out.\n\nDispatched to `drafts.remove` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_remove_draft`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "drafts"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/mutations:removeDraftFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "drafts.remove",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_remove_draft"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadId": {
                    "type": "string",
                    "description": "A Convex document id in the `campaignRunLeads` table."
                  },
                  "reason": {
                    "type": "string",
                    "description": "The caller's own words for why. Stored verbatim and shown on the row."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/drafts/update": {
      "post": {
        "operationId": "drafts_update",
        "summary": "drafts.update",
        "description": "Rewrite the draft on one run lead. Refuses a message that already went out; never sends.\n\nDispatched to `drafts.update` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_update_draft`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "drafts"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:updateRunLeadDraft` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "drafts.update",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_update_draft"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadId": {
                    "type": "string"
                  },
                  "subject": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/engine/start": {
      "post": {
        "operationId": "engine_start",
        "summary": "engine.start",
        "description": "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.\n\nDispatched 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it runs the discovery and research engine: web search, crawls, model calls, per-lead credits\n- MCP tools that wrap the same function: `selda_run_pipeline`\n- Filled in from your API key, never sent by you: `orgId`, `apiKeyId`",
        "tags": [
          "engine"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/index:startEngineFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "engine.start",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_engine_run",
        "x-selda-mcp-tools": [
          "selda_run_pipeline"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "idea": {
                    "type": "string"
                  },
                  "campaignId": {
                    "type": "string",
                    "description": "A Convex document id in the `campaigns` table."
                  },
                  "targetLeadCount": {
                    "type": "number"
                  },
                  "startAt": {
                    "type": "number"
                  },
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "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."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/events/ingest": {
      "post": {
        "operationId": "events_ingest",
        "summary": "events.ingest",
        "description": "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.\n\nDispatched to `events.ingest` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- Needs the `inboundIntake` entitlement on top of the scope\n- MCP tools that wrap the same function: `selda_ingest_event`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "events"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:ingestEvent` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "events.ingest",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-entitlement": "inboundIntake",
        "x-selda-mcp-tools": [
          "selda_ingest_event"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "description": "Caller-defined: \"form_submitted\", \"analysis_completed\", \"contract_signed\", anything."
                  },
                  "identity": {
                    "type": "object",
                    "properties": {
                      "email": {
                        "type": "string"
                      },
                      "domain": {
                        "type": "string"
                      },
                      "linkedinUrl": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "company": {
                        "type": "string"
                      },
                      "jobTitle": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      }
                    }
                  },
                  "payload": {
                    "description": "Whatever the event carried. Stored whole; only summarized into the notes line. Any JSON value. The validator is `v.any()`."
                  },
                  "source": {
                    "type": "string",
                    "description": "Which system reported it, e.g. \"evexcreative.com\". Stored as `discoverySource`."
                  },
                  "occurredAt": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      }
                    ],
                    "description": "ISO string or epoch ms. An unusable or out-of-range value falls back to now, and says so."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "Replay protection, unique per org. A repeat returns the first result unchanged."
                  },
                  "attachToRunId": {
                    "type": "string",
                    "description": "Put 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. A Convex document id in the `campaignRuns` table."
                  },
                  "analysis": {
                    "type": "string",
                    "description": "Research the caller already did, used by engineV3 as authoritative grounding."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "autoAdvance": {
                    "type": "boolean",
                    "description": "Write 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."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "type",
                  "identity"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/flows": {
      "get": {
        "operationId": "flows_list",
        "summary": "flows.list",
        "description": "The flows in a workspace: what runs when something arrives from outside, the steps in order, and whether each is switched on. Includes the workspace's flow instruction files.\n\nDispatched to `flows.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_flows`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:listFlows` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_flows"
        ]
      },
      "post": {
        "operationId": "flows_create",
        "summary": "flows.create",
        "description": "Create a flow: a trigger plus the steps to run when something arrives. Off unless you say otherwise. No step can send.\n\nDispatched to `flows.create` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_create_flow`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:createFlow` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.create",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_create_flow"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "trigger": {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "inbound_event",
                          "manual"
                        ]
                      },
                      "eventTypes": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "sources": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "required": [
                      "kind"
                    ]
                  },
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "understand",
                            "route",
                            "attach",
                            "draft_reply",
                            "notify"
                          ]
                        },
                        "skillId": {
                          "type": "string",
                          "description": "A Convex document id in the `flowSkills` table."
                        },
                        "categories": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "routes": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "runId": {
                          "type": "string",
                          "description": "A Convex document id in the `campaignRuns` table."
                        },
                        "emails": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "webhook": {
                          "type": "boolean"
                        },
                        "note": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "kind"
                      ]
                    }
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "Absent means off. A flow that starts answering the world the moment a script writes it is a decision the script made about somebody's website."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "name",
                  "trigger",
                  "steps"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/flows/{flowId}": {
      "delete": {
        "operationId": "flows_delete",
        "summary": "flows.delete",
        "description": "Delete a flow and its run log.\n\nDispatched to `flows.delete` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_delete_flow`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "parameters": [
          {
            "name": "flowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:removeFlow` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.delete",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_delete_flow"
        ]
      },
      "patch": {
        "operationId": "flows_update",
        "summary": "flows.update",
        "description": "Rewrite a flow's name, trigger or steps.\n\nDispatched to `flows.update` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_update_flow`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "parameters": [
          {
            "name": "flowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:updateFlow` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.update",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_update_flow"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "trigger": {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "type": "string",
                        "enum": [
                          "inbound_event",
                          "manual"
                        ]
                      },
                      "eventTypes": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "sources": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "required": [
                      "kind"
                    ]
                  },
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/flows/{flowId}/runs": {
      "get": {
        "operationId": "flows_runs",
        "summary": "flows.runs",
        "description": "What a flow actually did, run by run, step by step, including the steps that did nothing and why.\n\nDispatched to `flows.runs` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_flow_runs`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "parameters": [
          {
            "name": "flowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:flowRuns` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.runs",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_flow_runs"
        ]
      }
    },
    "/v1/flows/{flowId}/set-enabled": {
      "post": {
        "operationId": "flows_setEnabled",
        "summary": "flows.setEnabled",
        "description": "Switch a flow on or off.\n\nDispatched to `flows.setEnabled` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_set_flow_enabled`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "parameters": [
          {
            "name": "flowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:setFlowEnabled` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.setEnabled",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_set_flow_enabled"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "enabled"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/flows/save-skill": {
      "post": {
        "operationId": "flows_saveSkill",
        "summary": "flows.saveSkill",
        "description": "Write or rewrite an instruction file a flow step reads: how this business decides what an enquiry is.\n\nDispatched to `flows.saveSkill` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_save_flow_skill`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "flows"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `flows/mcp:saveSkill` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "flows.saveSkill",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_save_flow_skill"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "skillId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "name",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/inbox/add-message": {
      "post": {
        "operationId": "inbox_addMessage",
        "summary": "inbox.addMessage",
        "description": "Put one message into a lead's Sales Inbox thread, in either direction, even for somebody who was never in a campaign. Creates the lead if it is new. This can send nothing.\n\nDispatched to `inbox.addMessage` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_add_inbox_message`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "inbox"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addInboxMessage` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "inbox.addMessage",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_add_inbox_message"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "leadId": {
                    "type": "string",
                    "description": "An existing lead. Omit it and name the person in `identity` instead."
                  },
                  "identity": {
                    "type": "object",
                    "properties": {
                      "email": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "company": {
                        "type": "string"
                      },
                      "jobTitle": {
                        "type": "string"
                      },
                      "domain": {
                        "type": "string"
                      },
                      "linkedinUrl": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      }
                    }
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "inbound",
                      "outbound"
                    ],
                    "description": "`inbound` = they wrote it. `outbound` = we did, somewhere else. Nothing here sends."
                  },
                  "body": {
                    "type": "string"
                  },
                  "subject": {
                    "type": "string"
                  },
                  "occurredAt": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      }
                    ],
                    "description": "When it actually happened. Defaults to now; a wrong \"now\" on old history is a lie."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "direction",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/knowledge": {
      "get": {
        "operationId": "knowledge_get",
        "summary": "knowledge.get",
        "description": "What Selda knows about your business: the prose that grounds every message.\n\nDispatched to `knowledge.get` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_knowledge`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "knowledge"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `projects` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getKnowledge` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "knowledge.get",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_knowledge"
        ]
      }
    },
    "/v1/knowledge/append": {
      "post": {
        "operationId": "knowledge_append",
        "summary": "knowledge.append",
        "description": "Add to what Selda knows about your business.\n\nDispatched to `knowledge.append` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_append_knowledge`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "knowledge"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:appendKnowledge` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "knowledge.append",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_append_knowledge"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/knowledge/set": {
      "post": {
        "operationId": "knowledge_set",
        "summary": "knowledge.set",
        "description": "Replace what Selda knows about your business.\n\nDispatched to `knowledge.set` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_set_knowledge`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "knowledge"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:setKnowledge` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "knowledge.set",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_set_knowledge"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "text"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads": {
      "get": {
        "operationId": "leads_list",
        "summary": "leads.list",
        "description": "Leads in a workspace.\n\nDispatched to `leads.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "city",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hasEmail",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listLeads` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_leads"
        ]
      },
      "post": {
        "operationId": "leads_add",
        "summary": "leads.add",
        "description": "Add one company/contact. Pass `analysis` with research you already did and the message is written from it instead of a fresh crawl.\n\nDispatched to `leads.add` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_add_lead`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.add",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_add_lead"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "company": {
                    "type": "string"
                  },
                  "companyDomain": {
                    "type": "string"
                  },
                  "jobTitle": {
                    "type": "string"
                  },
                  "linkedinUrl": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "analysis": {
                    "type": "string"
                  },
                  "outreachAngle": {
                    "type": "string",
                    "description": "The opening the operator already chose, and why they chose the company. Both are read by the engine (`operatorAngle` governs the opening, `whyGoodLead` feeds research and the fit call). They were wired into the engine on 17.8.2026 and had no door on this mutation for an hour, which is the same shape of gap the analysis had for a week: read by a writer, unreachable by the caller."
                  },
                  "whyGoodLead": {
                    "type": "string"
                  },
                  "externalUrl": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "city": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string"
                  },
                  "customFields": {
                    "description": "Any JSON value. The validator is `v.any()`."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/{leadId}": {
      "delete": {
        "operationId": "leads_delete",
        "summary": "leads.delete",
        "description": "Remove one lead. Deleting is the caller's act, Selda never removes a lead on its own.\n\nDispatched to `leads.delete` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_delete_lead`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `leads` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:deleteLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.delete",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_delete_lead"
        ]
      },
      "get": {
        "operationId": "leads_get",
        "summary": "leads.get",
        "description": "One lead in full: research, fit, outreach angle, notes.\n\nDispatched to `leads.get` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_lead`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.get",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_lead"
        ]
      },
      "patch": {
        "operationId": "leads_update",
        "summary": "leads.update",
        "description": "Edit a lead's fields, including its status. Org-scoped, so an API key can reach it.\n\nDispatched to `leads.update` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_update_lead`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `leads` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:updateLeadInternal` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.update",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_update_lead"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notes": {
                    "type": "string"
                  },
                  "appendNote": {
                    "type": "boolean",
                    "description": "Add `notes` to what is already there instead of replacing it. Default false, because that is what this fn has always done over HTTP and a silent change of meaning would rewrite an integration's behaviour without it asking. The MCP tool `selda_update_lead` passes true unless told otherwise: its own description says \"notes to add\", and an integration writing a timeline one line at a time was getting a field with one line in it. A timeline that overwrites itself is not a timeline."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "status": {
                    "type": "string"
                  },
                  "analysis": {
                    "type": "string",
                    "description": "THE ANALYSIS IS CORRECTABLE, because a wrong fact in it is not cosmetic. `add_lead` writes `providedResearch` and nothing could edit it, so an analysis pushed with a false claim, \"the price is not shown\", when the prices were right there, was permanent, and it is the text Selda writes the message FROM. The wrong fact would have reached the customer with no way to stop it short of deleting the lead."
                  },
                  "campaignId": {
                    "type": "string"
                  },
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "jobTitle": {
                    "type": "string"
                  },
                  "companyDescription": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string"
                  },
                  "fitScore": {
                    "type": "number"
                  },
                  "outreachAngle": {
                    "type": "string"
                  },
                  "externalUrl": {
                    "type": "string"
                  },
                  "customFields": {
                    "description": "Any JSON value. The validator is `v.any()`."
                  },
                  "companyWebsite": {
                    "type": "string"
                  },
                  "companyDomain": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "dealStage": {
                    "anyOf": [
                      {
                        "type": "string",
                        "const": "proposal_sent"
                      },
                      {
                        "type": "string",
                        "const": "negotiation"
                      },
                      {
                        "type": "string",
                        "const": "won"
                      },
                      {
                        "type": "string",
                        "const": "lost"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Where the deal stands, set by the person or their agent; Selda never advances it itself."
                  },
                  "dealValueEur": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "What it is worth, in EUR. null clears a wrong figure."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/leads/{leadId}/add-alias": {
      "post": {
        "operationId": "leads_addAlias",
        "summary": "leads.addAlias",
        "description": "Claim another email address for a lead, so a reply from it lands in the same conversation. Also adopts that address's earlier unlinked inbound.\n\nDispatched to `leads.addAlias` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_add_lead_alias`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLeadAlias` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.addAlias",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_add_lead_alias"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "email"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/{leadId}/add-tag": {
      "post": {
        "operationId": "leads_addTag",
        "summary": "leads.addTag",
        "description": "Tag a lead.\n\nDispatched to `leads.addTag` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_tag_lead`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `leads` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLeadTag` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.addTag",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_tag_lead"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "tags"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/{leadId}/enrich": {
      "post": {
        "operationId": "leads_enrich",
        "summary": "leads.enrich",
        "description": "Enrich one lead from a natural-language instruction.\n\nDispatched to `leads.enrich` 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it resolves real people's contact details through a paid provider, which costs money per call\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `leads/enrichBatch:enrichLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.enrich",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_contact_data",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roles": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "language": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/leads/{leadId}/skip": {
      "post": {
        "operationId": "leads_skip",
        "summary": "leads.skip",
        "description": "DELETES a lead and every message on it (legacy path, Clerk-authenticated, an API key cannot reach this; use leads.delete instead).\n\nDispatched to `leads.skip` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- A legacy entry. Its summary above says what that costs you.",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `leads` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `leads/mutations:skipLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.skip",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed"
      }
    },
    "/v1/leads/{leadId}/status": {
      "patch": {
        "operationId": "leads_updateStatus",
        "summary": "leads.updateStatus",
        "description": "Set a lead's status (legacy path, Clerk-authenticated, an API key cannot reach this; the MCP tool uses the org-scoped leads.update).\n\nDispatched to `leads.updateStatus` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- A legacy entry. Its summary above says what that costs you.",
        "tags": [
          "leads"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `leads` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `leads/mutations:updateLeadStatus` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.updateStatus",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "new",
                      "contacted",
                      "responded",
                      "qualified",
                      "quote",
                      "unqualified"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "status"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/add-batch": {
      "post": {
        "operationId": "leads_addBatch",
        "summary": "leads.addBatch",
        "description": "Add many companies/contacts in one call.\n\nDispatched to `leads.addBatch` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_add_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:addLeadBatch` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.addBatch",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_add_leads"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "leads": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "firstName": {
                          "type": "string"
                        },
                        "lastName": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "company": {
                          "type": "string"
                        },
                        "companyDomain": {
                          "type": "string"
                        },
                        "domain": {
                          "type": "string"
                        },
                        "jobTitle": {
                          "type": "string"
                        },
                        "linkedinUrl": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "notes": {
                          "type": "string"
                        },
                        "analysis": {
                          "type": "string"
                        },
                        "externalUrl": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        },
                        "tags": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "city": {
                          "type": "string"
                        },
                        "country": {
                          "type": "string"
                        },
                        "location": {
                          "type": "string"
                        },
                        "companyCity": {
                          "type": "string"
                        },
                        "companyWebsite": {
                          "type": "string"
                        },
                        "customFields": {
                          "description": "Any JSON value. The validator is `v.any()`."
                        }
                      }
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "leads"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/delete-batch": {
      "post": {
        "operationId": "leads_deleteBatch",
        "summary": "leads.deleteBatch",
        "description": "Remove many leads.\n\nDispatched to `leads.deleteBatch` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_delete_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:deleteLeadBatch` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.deleteBatch",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_delete_leads"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "A Convex document id in the `leads` table."
                    }
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadIds"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/leads/enrich-batch": {
      "post": {
        "operationId": "leads_enrichBatch",
        "summary": "leads.enrichBatch",
        "description": "Enrich many leads from a natural-language instruction.\n\nDispatched to `leads.enrichBatch` 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it resolves real people's contact details through a paid provider, which costs money per call\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `leads/enrichBatch:enrichLeadBatch` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.enrichBatch",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_contact_data",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "leadIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "roles": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "language": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/leads/merge": {
      "post": {
        "operationId": "leads_merge",
        "summary": "leads.merge",
        "description": "Merge duplicate leads.\n\nDispatched to `leads.merge` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_merge_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "leads"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:mergeLead` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "leads.merge",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_merge_leads"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "primaryId": {
                    "type": "string",
                    "description": "A Convex document id in the `leads` table."
                  },
                  "duplicateId": {
                    "type": "string",
                    "description": "A Convex document id in the `leads` table."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "primaryId",
                  "duplicateId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/material/import": {
      "post": {
        "operationId": "material_import",
        "summary": "material.import",
        "description": "Your prospect folder → a campaign + company list, then it stops. Uploading material is not permission to send.\n\nDispatched to `material.import` 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.\n\n- Scope: `pipeline`\n- A free sandbox key may call it, but not with every argument. `autoAdvance` carries the run on into contact lookup and drafting, which spend, so with it the same call needs a live key.\n- MCP tools that wrap the same function: `selda_import_material`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "material"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/folderImportApi:importProspectMaterial` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "material.import",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "depends-on-arguments",
        "x-selda-mcp-tools": [
          "selda_import_material"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "path": {
                          "type": "string"
                        },
                        "storageId": {
                          "type": "string",
                          "description": "A Convex document id in the `_storage` table."
                        },
                        "sizeBytes": {
                          "type": "number"
                        },
                        "mimeType": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "path",
                        "storageId",
                        "sizeBytes"
                      ]
                    }
                  },
                  "assignments": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "folderName": {
                          "type": "string"
                        },
                        "domain": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "folderName",
                        "domain"
                      ]
                    }
                  },
                  "excludedFolders": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "campaignName": {
                    "type": "string"
                  },
                  "targetRunId": {
                    "type": "string",
                    "description": "Push 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. A Convex document id in the `campaignRuns` table."
                  },
                  "campaignBrief": {
                    "type": "string",
                    "description": "The 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."
                  },
                  "autoAdvance": {
                    "anyOf": [
                      {
                        "type": "boolean"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "leads",
                            "messages"
                          ]
                        }
                      }
                    ],
                    "description": "Explicit 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."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "files"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/material/upload": {
      "post": {
        "operationId": "material_upload",
        "summary": "POST /mcp/material/upload",
        "description": "Raw file bytes in, a `storageId` out. Send the file's path in `X-Selda-Path`: that path is how Selda maps a file to a company. Then hand the ids to `material.import`.\n\nThe request body is the file itself, not JSON.\n\n- Scope: `pipeline`\n- A free sandbox key may call it",
        "tags": [
          "material"
        ],
        "parameters": [
          {
            "name": "X-Selda-Path",
            "in": "header",
            "required": true,
            "description": "The file's path inside your folder, for example `boreo/filterit/analysis.pdf`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "type": "object",
                      "properties": {
                        "storageId": {
                          "type": "string"
                        },
                        "path": {
                          "type": "string"
                        },
                        "sizeBytes": {
                          "type": "number"
                        },
                        "mimeType": {
                          "type": "string"
                        }
                      }
                    },
                    "request_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No `X-Selda-Path`, or an empty body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the `pipeline` scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The file is over the 25 MB per-file limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-rpc-endpoint": "/mcp/material/upload",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_upload_material"
        ]
      }
    },
    "/v1/messages/{messageId}/approve": {
      "post": {
        "operationId": "messages_approve",
        "summary": "messages.approve",
        "description": "Approve a drafted message. Approval only. It does not send.\n\nDispatched to `messages.approve` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_approve_message`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "messages"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:approveMessage` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "messages.approve",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_approve_message"
        ]
      }
    },
    "/v1/messages/by-lead": {
      "get": {
        "operationId": "messages_byLead",
        "summary": "messages.byLead",
        "description": "The whole thread with one lead, sent and received.\n\nDispatched to `messages.byLead` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_thread`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "messages"
        ],
        "parameters": [
          {
            "name": "leadId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getThread` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "messages.byLead",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_thread"
        ]
      }
    },
    "/v1/messages/by-project": {
      "get": {
        "operationId": "messages_byProject",
        "summary": "messages.byProject",
        "description": "Messages in a workspace.\n\nDispatched to `messages.byProject` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_messages`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "messages"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Epoch ms. Only messages at or after this instant.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "outbound",
                "inbound"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listMessages` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "messages.byProject",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_messages"
        ]
      }
    },
    "/v1/messages/generate": {
      "post": {
        "operationId": "messages_generate",
        "summary": "messages.generate",
        "description": "Draft a message for a lead.\n\nDispatched to `messages.generate` 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.\n\n- Scope: `pipeline`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_generate_message`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "messages"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:generateMessage` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "messages.generate",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_generate_message"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadId": {
                    "type": "string"
                  },
                  "campaignId": {
                    "type": "string"
                  },
                  "outreachAngle": {
                    "type": "string"
                  },
                  "channel": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "operationId": "projects_list",
        "summary": "projects.list",
        "description": "Your workspaces. Start here. Every other fn needs a projectId.\n\nDispatched to `projects.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_projects`\n- Filled in from your API key, never sent by you: `orgId`, `userId`, `boundProjectId`",
        "tags": [
          "projects"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listProjects` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "projects.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_projects"
        ]
      }
    },
    "/v1/projects/{projectId}": {
      "get": {
        "operationId": "projects_get",
        "summary": "projects.get",
        "description": "One workspace in full: business context, market analysis, ICP, settings.\n\nDispatched to `projects.get` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_project`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "projects"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getProject` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "projects.get",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_project"
        ]
      }
    },
    "/v1/projects/{projectId}/context": {
      "patch": {
        "operationId": "projects_updateContext",
        "summary": "projects.updateContext",
        "description": "Rewrite a workspace's business context.\n\nDispatched to `projects.updateContext` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_update_project_context`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "projects"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:updateProjectContext` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "projects.updateContext",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_update_project_context"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "whatTheyDo": {
                    "type": "string"
                  },
                  "industry": {
                    "type": "string"
                  },
                  "valueProposition": {
                    "type": "string"
                  },
                  "products": {
                    "type": "string"
                  },
                  "targetMarkets": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "jobTitles": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "companySize": {
                    "type": "string"
                  },
                  "voiceExamplesGood": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "voiceExamplesBad": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "forbiddenPhrases": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "toneWords": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "competitors": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "knownCustomers": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/v1/replies/classify": {
      "post": {
        "operationId": "replies_classify",
        "summary": "replies.classify",
        "description": "Classify inbound replies.\n\nDispatched to `replies.classify` 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.\n\n- Scope: `pipeline`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_classify_replies`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "replies"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:classifyReplies` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "replies.classify",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_classify_replies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/replies/draft": {
      "post": {
        "operationId": "replies_draft",
        "summary": "replies.draft",
        "description": "Write a reply draft into a lead's Sales Inbox thread. A person reviews and sends it in the app, this can send nothing.\n\nDispatched to `replies.draft` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_draft_reply`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "replies"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:draftInboxReply` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "replies.draft",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_draft_reply"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "leadId": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "instruction": {
                    "type": "string",
                    "description": "What the person asked for in their own words (\"tee tästä lyhyempi\"). Optional, kept only in the draft history beside the text, it changes nothing about what is stored in the composer. The `selda_draft_reply` tool schemas in `api/mcp.ts` and `mcp-server/index.ts` do not pass it yet, so this arrives empty on the tool path until they do."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "leadId",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/replies/preview": {
      "post": {
        "operationId": "replies_preview",
        "summary": "replies.preview",
        "description": "Ask how Selda would answer an enquiry, from this workspace's Brain, without creating a lead or storing a draft. Same writer the real reply uses, so tuning against this tunes the real thing. Stores nothing and sends nothing.\n\nDispatched to `replies.preview` 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.\n\n- Scope: `pipeline`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_preview_reply`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "replies"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `salesInbox/inboundAutoDraft:previewInboundReply` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "replies.preview",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_preview_reply"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "eventType": {
                    "type": "string",
                    "description": "What kind of thing arrived: \"quote_requested\", \"guide_downloaded\", anything you use."
                  },
                  "payload": {
                    "description": "The form as you would send it, keys and all. Exactly what `events.ingest` takes. Any JSON value. The validator is `v.any()`."
                  },
                  "who": {
                    "type": "string",
                    "description": "Who it is from, for the greeting. Optional, an anonymous enquiry is a real case."
                  },
                  "company": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string"
                  },
                  "language": {
                    "type": "string",
                    "description": "Force a language instead of letting the enquiry decide. For checking one deliberately."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "eventType"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/runs": {
      "get": {
        "operationId": "runs_list",
        "summary": "runs.list",
        "description": "Every campaign run in a project, newest first, with its status. Use it to find a runId you no longer have. Runs the human archived are left out; pass includeArchived: true to see them too.\n\nDispatched to `runs.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_runs`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "includeArchived",
            "in": "query",
            "required": false,
            "description": "Show the runs the human archived as well. Default false, see the note in the handler. It exists because this door's whole reason to exist is that a runId must stay findable, and a filter with no way past it would take that back for exactly the runs somebody tidied away. Archiving hides; it has never deleted anything.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:listRuns` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_runs"
        ]
      }
    },
    "/v1/runs/{runId}/archive": {
      "post": {
        "operationId": "runs_archive",
        "summary": "runs.archive",
        "description": "Close a campaign run and take it off the active list. Keeps every contact and every message, deleting contacts stays a human act in the app.\n\nDispatched to `runs.archive` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_archive_run`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `campaignRuns` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/mutations:archiveRunFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.archive",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_archive_run"
        ]
      }
    },
    "/v1/runs/{runId}/confirm-companies": {
      "post": {
        "operationId": "runs_confirmCompanies",
        "summary": "runs.confirmCompanies",
        "description": "Confirm a run's company list so Selda finds the decision-makers and drafts the messages. Spends credits. Sends nothing, the send is still a human press in the app.\n\nDispatched to `runs.confirmCompanies` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key is refused: it runs the discovery and research engine: web search, crawls, model calls, per-lead credits\n- MCP tools that wrap the same function: `selda_confirm_companies`\n- Filled in from your API key, never sent by you: `orgId`\n- A legacy entry. Its summary above says what that costs you.",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `campaignRuns` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/mutations:confirmProfileFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.confirmCompanies",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_engine_run",
        "x-selda-mcp-tools": [
          "selda_confirm_companies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "language": {
                    "type": "string"
                  },
                  "languageChosen": {
                    "type": "boolean",
                    "description": "DID THE HUMAN NAME THAT LANGUAGE at this gate, or is it Selda's proposal riding through? The card sends a language on every confirm, whether or not anyone touched the field, and the engine read the mere presence of `confirmedProfile.language` as \"the operator answered the question\", then enforced it over every company's own evidence and told them they had chosen it. Absent = an older client that does not send it; the presence of a language is then read as before, and simply never attributed to the human."
                  },
                  "channel": {
                    "type": "string"
                  },
                  "targetAudience": {
                    "type": "string"
                  },
                  "targetRoles": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "targetDecisionMakers": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "title": {
                          "type": "string"
                        },
                        "why": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "title"
                      ]
                    }
                  },
                  "targetMarket": {
                    "type": "string"
                  },
                  "greeting": {
                    "type": "string"
                  },
                  "tone": {
                    "type": "string"
                  },
                  "leadsPerCompany": {
                    "type": "string"
                  },
                  "excludedDomains": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "researchCount": {
                    "type": "number"
                  },
                  "clarifyingAnswers": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "question": {
                          "type": "string"
                        },
                        "answer": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "question",
                        "answer"
                      ]
                    }
                  },
                  "contactLookups": {
                    "type": "object",
                    "properties": {
                      "phone": {
                        "type": "boolean"
                      },
                      "linkedin": {
                        "type": "boolean"
                      }
                    },
                    "description": "WHAT THIS SEARCH IS ASKED TO LOOK UP BEYOND AN ADDRESS, the human's answers, #127. Absent = an older client, and then `normalizeContactLookups` supplies the defaults, which are written to reproduce what those runs already did. Neither switch controls KEEPING anything. Every phone number and every profile URL a rung already running happens to publish is stored either way, dropping one to honour a switch would be Selda throwing away a fact nobody asked it to throw away. What they control is whether Selda spends anything going to look."
                  },
                  "contactResearchRequest": {
                    "type": "string",
                    "description": "What else the human wants found out about the decision-maker, in their own words. A real query, not a note: `engineV3` runs one web search per named contact with exactly this question, stores the answer on the lead and hands it to the composer. Empty = nothing extra is searched for and nothing extra is spent."
                  }
                },
                "additionalProperties": false,
                "required": [
                  "language",
                  "channel"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/runs/{runId}/leads": {
      "get": {
        "operationId": "runs_leads",
        "summary": "runs.leads",
        "description": "The companies a run found, each with the message Selda drafted for it. Nothing is sent.\n\nDispatched to `runs.leads` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_run_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "withDraftOnly",
            "in": "query",
            "required": false,
            "description": "Only rows that actually carry a draft. Default false, the caller usually wants everything.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getRunLeads` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.leads",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_run_leads"
        ]
      }
    },
    "/v1/runs/{runId}/rename": {
      "post": {
        "operationId": "runs_rename",
        "summary": "runs.rename",
        "description": "Give a campaign run a name a person would recognise. An empty name restores the derived title.\n\nDispatched to `runs.rename` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_rename_campaign`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `campaignRuns` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/mutations:renameRunFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.rename",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_rename_campaign"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/runs/{runId}/status": {
      "get": {
        "operationId": "runs_status",
        "summary": "runs.status",
        "description": "Status of one campaign run: phase, companies found, contacts resolved, drafts written, errors.\n\nDispatched to `runs.status` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_get_run_status`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `mcpQueries:getRunStatus` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.status",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_get_run_status"
        ]
      }
    },
    "/v1/runs/start-from-leads": {
      "post": {
        "operationId": "runs_startFromLeads",
        "summary": "runs.startFromLeads",
        "description": "Start a campaign from leads already pushed in with selda_add_lead, selected by the source label you gave them. No discovery, Selda writes a message per lead from the analysis that came with it, and stops at the drafts.\n\nDispatched to `runs.startFromLeads` 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.\n\n- Scope: `pipeline`\n- A free sandbox key is refused: it runs the discovery and research engine: web search, crawls, model calls, per-lead credits\n- MCP tools that wrap the same function: `selda_start_campaign_from_leads`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "runs"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `campaignRunner/importLeads:startRunFromLeadsFromApiKey` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "runs.startFromLeads",
        "x-selda-rpc-endpoint": "/mcp/run",
        "x-selda-scope": "pipeline",
        "x-selda-sandbox": "refused",
        "x-selda-live-only-reason": "paid_engine_run",
        "x-selda-mcp-tools": [
          "selda_start_campaign_from_leads"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "description": "A Convex document id in the `projects` table."
                  },
                  "source": {
                    "type": "string"
                  },
                  "sourceDisplayLabel": {
                    "type": "string"
                  },
                  "campaignId": {
                    "type": "string",
                    "description": "A Convex document id in the `campaigns` table."
                  },
                  "campaignBrief": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "source"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "operationId": "webhooks_list",
        "summary": "webhooks.list",
        "description": "Outbound webhook endpoints registered for this workspace.\n\nDispatched to `webhooks.list` on `POST /mcp/query`. 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.\n\n- Scope: `read`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_list_webhooks`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "webhooks"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `integrations/outboundWebhooks:listWebhooks` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "webhooks.list",
        "x-selda-rpc-endpoint": "/mcp/query",
        "x-selda-scope": "read",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_list_webhooks"
        ]
      },
      "post": {
        "operationId": "webhooks_create",
        "summary": "webhooks.create",
        "description": "Register an endpoint for events like reply.received.\n\nDispatched to `webhooks.create` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_create_webhook`\n- Filled in from your API key, never sent by you: `orgId`, `userId`",
        "tags": [
          "webhooks"
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `integrations/outboundWebhooks:createWebhook` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "webhooks.create",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_create_webhook"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "url",
                  "events"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{webhookId}": {
      "delete": {
        "operationId": "webhooks_delete",
        "summary": "webhooks.delete",
        "description": "Remove a webhook endpoint.\n\nDispatched to `webhooks.delete` on `POST /mcp/mutate`. 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.\n\n- Scope: `write`\n- A free sandbox key may call it\n- MCP tools that wrap the same function: `selda_delete_webhook`\n- Filled in from your API key, never sent by you: `orgId`",
        "tags": [
          "webhooks"
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A Convex document id in the `workspaceWebhooks` table."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "value",
                    "request_id"
                  ],
                  "properties": {
                    "value": {
                      "description": "Not documented. `integrations/outboundWebhooks:deleteWebhook` declares no `returns` validator, so there is nothing to derive a shape 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."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Echoed in the `X-Request-Id` header."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown route, or arguments the validator refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, the plan entitlement, or is a sandbox key on a live-only function.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for the next minute window and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The function threw. The message says what, without leaking internals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-selda-fn": "webhooks.delete",
        "x-selda-rpc-endpoint": "/mcp/mutate",
        "x-selda-scope": "write",
        "x-selda-sandbox": "allowed",
        "x-selda-mcp-tools": [
          "selda_delete_webhook"
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Selda API key. Created in Settings, stored hashed, shown once, revocable."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "request_id"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "description": "The coarse class, for branching on."
              },
              "code": {
                "type": "string",
                "description": "The specific machine-readable code."
              },
              "message": {
                "type": "string",
                "description": "Sanitized. It never leaks internals."
              },
              "request_id": {
                "type": "string"
              }
            }
          },
          "request_id": {
            "type": "string",
            "description": "Echoed in the `X-Request-Id` header."
          }
        }
      }
    }
  }
}
