# Lead context and notes


Use the conversation's `threadId` to read its campaign contacts, including
custom variables, LinkedIn URLs, and known recipient provider and security
gateway. Each enrollment appears separately; campaign fields take precedence
over shared lead fields. Use the returned `leadId` for notes and emails.
Conversations without a linked campaign contact return an empty contact list.

| REST operation | Required permission |
| --- | --- |
| `GET /v1/unibox/threads/{threadId}/contacts` | `unibox:read`, `lead:read` |
| `GET /v1/workspaces/{workspaceId}/unibox/threads/{threadId}/label-history` | `unibox:read` |
| `GET /v1/leads/{leadId}/notes` | `lead:read` |
| `POST /v1/leads/{leadId}/notes` | `lead:update` |
| `DELETE /v1/leads/{leadId}/notes/{noteId}` | `lead:update` |
| `POST /v1/leads/{leadId}/emails` | `lead:update` |

Notes belong to the shared lead and are internal plain text. Create one with
`{"body":"Requested a follow-up next week."}` (1–20,000 characters after
trimming); notes are never sent to the lead. API-key-created notes have no human
author attribution. Deleting a note requires its owning lead ID.

Add an associated email with `{"email":"alternate@example.com"}`. This preserves
the existing primary address. Repeating an address is a no-op and does not
reactivate an address marked bad. An address belonging to another lead in the
workspace returns a conflict. Active associated emails are eligible for the
campaign email waterfall.

Notes return `items` and `nextCursor`, newest first, with up to 50 entries.
See the [API reference](/rest-api/reference) for query parameter schemas.

## Conversation label history

For MCP-only clients (including Composio), first call `list_unibox_threads`:

```json
{
  "name": "list_unibox_threads",
  "arguments": {
    "workspaceId": "11111111-1111-4111-8111-111111111111",
    "folder": "inbox",
    "search": "lead@example.com",
    "showAllEmails": true,
    "limit": 20
  }
}
```

Search is optional. The list defaults to the inbox and 20 threads per page;
`limit` accepts 1–100. Use another `folder` for archived, sent, starred, trash,
or warmup conversations. Pass the list's opaque `nextCursor` unchanged as
`cursor` to continue browsing.

`showAllEmails` is optional and defaults to `false`, limiting the inbox to managed
conversations. Set it to `true`, as in the example, to include other inbound inbox
mail, such as manually labeled conversations with no managed-send evidence.
Other folders retain their usual rules. Workspace permissions, search, and
pagination still apply. This option affects discovery, not stored history.

Select an item and pass its `id` (local UUID) as `threadId`, and its
`organizationId` (owning Workspace UUID) as `workspaceId`, to the history tool.
The illustrative UUID below must be replaced with that returned `id`;
`providerThreadId` is not a valid substitute.

```json
{
  "name": "list_thread_label_history",
  "arguments": {
    "workspaceId": "11111111-1111-4111-8111-111111111111",
    "threadId": "22222222-2222-4222-8222-222222222222",
    "limit": 50
  }
}
```

The tool returns `{ "items": [...], "nextCursor": "..." }`. Each item contains
`id`, `threadId`, `labelId`, `labelType`, `labelName`, `hexColor`, `action`,
`source`, `actorUserId`, `actorName`, and `occurredAt`. Actor fields can be null.
Pass the opaque `nextCursor` unchanged as `arguments.cursor`; null means no
more events. The default page size is 50; `limit` accepts 1–100.
A Workspace/thread mismatch is rejected even if you can access both Workspaces.

REST clients can discover the same IDs with
`GET /v1/workspaces/{workspaceId}/unibox/threads`. The history route is
`GET /v1/workspaces/{workspaceId}/unibox/threads/{threadId}/label-history`. For example:

```sh
curl --get 'https://api.schedulerzero.com/v1/workspaces/11111111-1111-4111-8111-111111111111/unibox/threads/22222222-2222-4222-8222-222222222222/label-history' \
  --header 'x-api-key: YOUR_API_KEY' \
  --data-urlencode 'limit=50'
```

REST and MCP return the same flat items and opaque `nextCursor`; pass it
unchanged as REST's `cursor` query parameter or MCP's `arguments.cursor`.
There is no MCP response wrapper or cursor conversion. The required Workspace
path and local thread UUID are checked by the existing authorized API handler.
The older `/v1/unibox/threads/...` route and its object cursor are not mounted
by the production Effect API and must not be used for this tool.

These are **conversation/thread reply labels**, and several labels can coexist.
Each event records an **added** or **removed** label, with its name and color
snapshotted at the time, source, timestamp, and human actor when available.
They are not an atomic single-label `from`/`to` record or a single-valued lead
status. A removal of A and addition of B remain two events; other labels may
still apply. Tracking has no pre-tracking backfill, so a complete historical
label set cannot be reconstructed reliably. Renaming or deleting a label does
not rewrite its stored name/color; actor names reflect the current user record.

