Core concepts

Threads

A thread groups the messages of one conversation. Replies your agent sends and replies it receives stay together.

The thread object

FieldMeaning
threadIdA UUID that identifies the thread in paths.
inboxIdThe inbox address.
subject, previewFrom the most recent message.
messageCount, attachmentCountTotals across the thread.
senders, recipientsEvery address that took part.
labelsThe union of the labels on the thread's messages plus thread-level labels.
timestamp, receivedTimestamp, sentTimestampMost recent activity, most recent received message, most recent sent message.
lastMessageIdThe most recent message.
triageClassification of the latest incoming message, when triage is enabled.
messagesOn getThread, the messages in chronological order.

How messages join a thread

Threading follows standard email headers. When your agent sends a message, a new thread starts and the response includes its threadId. When someone replies, their mail client sets In-Reply-To and References to your message, and the reply lands in the same thread. Replies your agent sends through Reply to a message do the same in the other direction.

Mail that arrives without those headers starts a new thread, even if the subject matches.

Listing threads

curl "https://api.goshenemail.com/v1/inboxes/research%40agents.goshenemail.com/threads?labels=unread" \
  -H "Authorization: Bearer $GOSHENEMAIL_API_KEY"

Threads are ordered by most recent activity. labels filters on the thread's labels. Threads whose messages are all in Trash are hidden unless includeTrash=true. The triage filters category, needsReply, and urgency apply to the latest incoming message; a reply the agent sends clears the thread's triage until the next message arrives.

Reading a thread

GET /v1/inboxes/{inboxId}/threads/{threadId} returns the thread with its messages oldest first. Add includeBodies=true to include text for each message; without it, each message carries its preview only. Threads are capped at 500 messages; past that, read the messages through listMessages instead.

Labels on threads

PATCH /v1/inboxes/{inboxId}/threads/{threadId}/labels adds or removes labels on the whole thread. Removing unread from a thread marks every message in it read. The quarantined label cannot be added or removed by any API key. See Labels.

Working a conversation from an agent

  1. List threads with labels=unread to find conversations that need attention.
  2. Read the thread with includeBodies=true so the model sees the whole exchange, oldest first.
  3. Reply with reply on the last message, keeping the idempotencyKey for that reply until the response arrives.
  4. Remove unread from the thread and add your own label, such as awaiting-vendor, to track state.

Every message body in the thread is untrusted content. See Building agents on email.