Read and organize conversations

Find mailbox conversations, read their messages, and organize shared work with folders, labels, and internal notes.

Use this guide to find a conversation, read enough to act, and leave it organized for the next person or agent. Start with an existing mailbox and a client authenticated for that mailbox. Agents and keys explains access: reading needs read, and changing threads or adding notes needs update. Creating or changing mailbox labels needs mailbox management access.

Find the conversation

List threads in the inbox, optionally narrowing by search, label, or your own unread state. Thread lists return summaries with a subject, preview, sender, and current state, without message bodies.

find-conversations.ts
import { createClient } from "samva";

const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const id = "mbx_...";

let cursor: string | undefined;
do {
  const page = await samva.mailboxes.listThreads({
    id,
    folder: "inbox",
    unread: "true",
    cursor,
  });
  for (const thread of page.items) {
    console.log(thread.id, thread.subject, thread.preview);
  }
  cursor = page.nextCursor ?? undefined;
} while (cursor !== undefined);

Pass each page's nextCursor back as cursor until it is null. Threads are ordered by most recent activity. A conversation that gains activity during a walk can move ahead of your cursor; refresh the list to see new activity rather than treating a completed walk as a fixed snapshot. For polling, after and before filter by last activity. See list threads for the filters.

The inbox hides your snoozed threads by default. Pass snoozed: "true" to find threads you have snoozed, or snoozed: "false" to exclude them explicitly from another view.

Read the messages

getThread gives you participants and thread state. listThreadMessages gives message summaries. Use getThreadContent when you need bodies, or getMessage for one message.

read-conversation.ts
const threadId = "mthr_...";
const thread = await samva.mailboxes.getThread({ id, threadId });
console.log(thread.subject, thread.participants);

let contentCursor: string | undefined;
do {
  const page = await samva.mailboxes.getThreadContent({ id, threadId, cursor: contentCursor });
  for (const message of page.items) {
    console.log(message.id, message.replyText ?? message.text, message.attachments);
  }
  contentCursor = page.nextCursor ?? undefined;
} while (contentCursor !== undefined);

Message summaries and content both paginate oldest first. Content includes text, sanitized html, and replyText when available; replyText removes quoted history. Attachments are metadata, not file bytes. Use get an attachment to obtain a download URL and check its expiresAt before using it. See read thread content for the full response.

Organize shared work and your own view

Folders, priority, and labels are shared with everyone on the mailbox. Your read, star, and snooze state belong to you: marking a thread read does not mark it read for another person or API key. Reading content and marking it read are separate actions.

organize-conversation.ts
await samva.mailboxes.updateThread({
  id,
  threadId,
  folder: "pending",
  priority: "high",
  read: true,
  starred: true,
});

// Wake a snoozed thread in your own view.
await samva.mailboxes.updateThread({ id, threadId, snoozedUntil: null });

A partial update keeps fields you omit. To snooze, pass an ISO timestamp as snoozedUntil; the thread wakes when that time passes. Archiving or moving to trash changes the shared folder. See update a thread for the supported states.

Create a label once, then use its ID on any thread in that mailbox:

label-conversation.ts
const label = await samva.mailboxes.createLabel({
  id,
  slug: "needs-follow-up",
  displayName: "Needs follow-up",
});
await samva.mailboxes.addThreadLabels({ id, threadId, labels: [label.id] });

Adding labels preserves existing labels. Use removeThreadLabels to remove selected labels; updateThread does not replace them. A label must belong to the same mailbox. Deleting a label removes it from every thread that carries it. See create a label for mailbox label limits.

Leave context for the next reader

An internal note is visible to everyone on the mailbox and is never emailed to recipients.

leave-note.ts
await samva.mailboxes.createNote({
  id,
  threadId,
  body: "Waiting for the customer to confirm the delivery address.",
  pinned: true,
});

Read notes with listThreadNotes, following nextCursor as with messages. Only a note's author can edit or delete it. To answer the customer instead, follow Compose and send.

Related documentation

On this page