---
title: "leads.update"
method: PATCH
path: "/v1/leads/{leadId}"
tags: ["leads"]
---

# leads.update

`PATCH /v1/leads/{leadId}`

## Request

- Base URL: `https://api.selda.ai`
- URL: `https://api.selda.ai/v1/leads/{leadId}`
- Auth: HTTP bearer

Edit a lead's fields, including its status. Org-scoped, so an API key can reach it.

Dispatched 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.

- Scope: `write`
- A free sandbox key may call it
- MCP tools that wrap the same function: `selda_update_lead`
- Filled in from your API key, never sent by you: `orgId`

## Path parameters

- `leadId` string, required: A Convex document id in the `leads` table.

## Request body

- object
  - `notes` string
  - `appendNote` boolean: 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` string[]
  - `status` string
  - `analysis` string: 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` string
  - `firstName` string
  - `lastName` string
  - `email` string
  - `jobTitle` string
  - `companyDescription` string
  - `city` string
  - `country` string
  - `location` string
  - `fitScore` number
  - `outreachAngle` string
  - `externalUrl` string
  - `customFields` unknown
  - `companyWebsite` string
  - `companyDomain` string, nullable
  - `dealStage` union: Where the deal stands, set by the person or their agent; Selda never advances it itself.
    - 'proposal_sent'
    - 'negotiation'
    - 'won'
    - 'lost'
  - `dealValueEur` number, nullable: What it is worth, in EUR. null clears a wrong figure.

## Response `200`

The call succeeded.

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

## Other responses

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

---

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