Core concepts
Messages
A message is one email, sent or received. Read, list, search, and label messages in an inbox.
The message object
| Field | Meaning |
|---|---|
messageId | The RFC 5322 Message-ID. Identifies the message in paths; URL encode it. |
threadId | The thread this message belongs to. See Threads. |
inboxId | The inbox address. |
from, to, cc, bcc, replyTo | Addresses. cc, bcc, and replyTo appear when present. |
subject, preview | The subject and a short plain-text preview. |
timestamp | When the message was sent or received, ISO 8601. |
labels | System and custom labels. See Labels. |
attachments | attachmentId, filename, contentType, and size for each file. See Attachments. |
text, html | Bodies. text is included on reads; html is returned on getMessage when you ask for it. |
protection | Scanner results for incoming mail: SPF, DKIM, DMARC, spam score, antivirus, and quarantine status. See Quarantine. |
triage | Optional classification of incoming mail. See Triage. |
delivery | For sent mail, per-recipient delivery outcomes. See below. |
Listing messages
curl "https://api.goshenemail.com/v1/inboxes/research%40agents.goshenemail.com/messages?labels=received&labels=unread&limit=20" \
-H "Authorization: Bearer $GOSHENEMAIL_API_KEY"
Lists are newest first. labels repeats for each label and matches messages that carry all of them. Pages hold up to 100 messages (default 20). When triage is enabled, category, needsReply, and urgency filter on the classification.
Quarantined messages are left out of lists and searches unless labels includes quarantined. Trashed messages are listed only when labels includes trash.
Searching messages
GET /v1/inboxes/{inboxId}/messages/search?query=invoice runs a full-text search over one inbox. query is 1 to 1000 characters and supports the usual web-search syntax: quoted phrases, - to exclude a word, or between alternatives. Results are ranked by relevance, then by time. The same page and triage filters apply.
Reading a message
GET /v1/inboxes/{inboxId}/messages/{messageId} returns the full message. Plain text is always present when the sender included it. Message IDs contain <, >, and @, so encode them: %3C20260920.1@example.net%3E.
A mailbox key cannot read the body of a quarantined message. Account keys see the message with its protection block, and the dashboard is where a person decides whether to release it.
Sent mail and delivery outcomes
Messages you send carry the sent label. Once the receiving server answers, the message gains a delivery block:
{
"delivery": {
"sentAt": "2026-09-20T18:05:12.000Z",
"updatedAt": "2026-09-20T18:05:14.000Z",
"recipients": [
{ "recipient": "vendor@example.net", "status": "delivered", "delivered": true, "smtpStatusCode": "250", "deliveryTimeMs": 1830 }
]
}
}
status per recipient is one of queued, accepted, delivered, deferred, bounced, failed, rejected, or complained, and reflects what the remote server reported. A 250 from the recipient's server means it accepted the message; it does not prove inbox placement or that anyone read it. Bounces carry the server's reason and SMTP status codes.
Marking as read
Reading a message through the API does not remove unread. Remove it explicitly when your agent has handled the message:
curl "https://api.goshenemail.com/v1/inboxes/research%40agents.goshenemail.com/messages/%3Cid%40example.net%3E/labels" \
-H "Authorization: Bearer $GOSHENEMAIL_API_KEY" -X PATCH \
-H "Content-Type: application/json" \
-d '{"removeLabels":["unread"],"addLabels":["handled"]}'
Untrusted content
Everything inside a message was written by someone else: the subject, the sender's display name, the body, and every attachment. Passing a message body to a model without framing it as data is how prompt injection happens. See Building agents on email.