Skip to main content

Notes on threads

Leave a note on a thread for whoever picks it up. Notes are never sent to anyone outside your team.

A note is a message for your team, kept on a thread beside its mail: what you already tried, who is waiting for an answer, what the next person should know. Notes are never sent. They are not part of any email, they are not in the thread’s replies or forwards, and they never leave mails.ai.

People write notes in the dashboard, and your code or agent writes them through the API.

In the dashboard

Open a thread. Its notes are listed under the mail, oldest first, each with its author and when it was written. Type in the note box under them to add one. Only your team sees notes.

Add a note

POST /v1/threads/{id}/notes takes a body of 1 to 10,000 characters and answers 201 with the new note. A thread holds up to 500 notes: past that the API answers 409 resource_cap_exceeded until one is deleted. A test key cannot add a note to a thread that holds live mail: that answers 403 live_mode_required.

import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY
const threadId = "thrd_01J9X4B6T1";

const note = await client.threads.notes.create(threadId, {
  body: "Customer was refunded on Oct 29. Waiting on their bank.",
});
console.log(note.id);
{
  "id": "note_01J9X6Q4W7",
  "object": "note",
  "thread_id": "thrd_01J9X4B6T1",
  "body": "Customer was refunded on Oct 29. Waiting on their bank.",
  "author": { "kind": "key", "id": "key_01J9X0Z9Y8" },
  "created_at": "2026-10-30T09:20:41.000Z"
}

The note object

FieldDescription
idstring The note’s id (note_…).
objectstring Always note.
thread_idstring The thread the note is on.
bodystring What the note says, 1 to 10,000 characters.
authorobject Who wrote it. kind is user for a person, with their id, email and name, or key for an API key or a connected app, with its id. If a person’s account is erased, their notes stay and their id becomes null.
created_atstring When the note was written.

List a thread’s notes

GET /v1/threads/{id}/notes answers { "data": [...] } with every note on the thread, oldest first. A thread from the API also carries notes_count, so you can tell which threads have notes without asking for each.

import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const { data: notes } = await client.threads.notes.list("thrd_01J9X4B6T1");
console.log(notes.map((n) => n.body));

Delete a note

Deleting a note removes it for good. An API key with the manage scope, or a workspace owner or admin in the dashboard, can delete any note. Any other member deletes only the notes they wrote: anyone else’s answers 403 insufficient_scope. A test key cannot delete a note on a thread that holds live mail: that answers 403 live_mode_required.

import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

await client.threads.notes.remove("thrd_01J9X4B6T1", "note_01J9X6Q4W7");
{ "id": "note_01J9X6Q4W7", "object": "note", "deleted": true }

Events

Adding a note emits thread.note_added with the thread, its agent, the note’s id and its author. The note’s words are not in the event, so they never reach a webhook endpoint: read them with GET /v1/threads/{id}/notes if your code needs them. A webhook endpoint receives the event only when its event_types names it, as with the other thread events. Once an author’s account is erased, their id, email and name are null in the event.

{
  "id": "evt_01J9X6R5X8",
  "type": "thread.note_added",
  "workspace_id": "wsk_01J9X0A1B2",
  "agent_id": "agt_01J9X1C3V5",
  "thread_id": "thrd_01J9X4B6T1",
  "test_mode": false,
  "data": {
    "thread_id": "thrd_01J9X4B6T1",
    "agent_id": "agt_01J9X1C3V5",
    "note_id": "note_01J9X6Q4W7",
    "author": { "kind": "key", "id": "key_01J9X0Z9Y8" }
  },
  "created_at": "2026-10-30T09:20:41.000Z"
}

Routes

Next steps

Full request and response shapes are in the API reference.

Was this page helpful?