Inbox
The inbox endpoints read the messages that arrived in the mailboxes of the workspace, with the full text, and send an answer. Replies returns the same messages with a snippet and is the place to correct a classification.
| Method | Path | Action |
|---|---|---|
GET |
/api/v1/inbox/messages |
List messages with the full text |
POST |
/api/v1/inbox/messages/{id}/reply |
Answer a message |
List messages#
| Parameter | Type | Notes |
|---|---|---|
category |
string | One of interested, neutral, not_interested, unclassified, autoresponder. |
unread |
string | true for unread messages only, false for read ones. |
since |
ISO 8601 | Messages received at or after this time. |
campaign_id |
UUID | Messages in reply to one campaign. |
prospect_id |
UUID | Messages from one prospect. |
limit |
integer | 1 to 100. Default 50. |
cursor |
string | See Pagination. |
Without category, the list leaves out autoresponders, like the All replies folder in the app. Newest messages come first.
curl "https://app.anymail.watch/api/v1/inbox/messages?unread=true&category=interested" \
-H "Authorization: Bearer $ANYMAIL_TOKEN"
const res = await fetch("https://app.anymail.watch/api/v1/inbox/messages?unread=true&category=interested", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ANYMAIL_TOKEN}`,
},
});
console.log(res.status, await res.text());
import os
import requests
response = requests.request(
"GET",
"https://app.anymail.watch/api/v1/inbox/messages?unread=true&category=interested",
headers={
"Authorization": f"Bearer {os.environ['ANYMAIL_TOKEN']}",
},
)
print(response.status_code, response.text)
Response 200
{
"data": [
{
"id": "20000000-0000-0000-0000-000000000001",
"prospect": { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "email": "ada@example.com", "first_name": "Ada", "last_name": null },
"company": { "id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427", "name": "Example Ltd", "domain": "example.com" },
"campaign": { "id": "3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "name": "Q3 outbound" },
"mailbox_id": "b2c3d4e5-0000-4000-8000-000000000001",
"from_email": "ada@example.com",
"subject": "Re: Quick question",
"classification": "interested",
"classification_overridden": false,
"is_autoresponder": false,
"is_read": false,
"received_at": "2026-09-03T10:00:00.000+00:00",
"snippet": "Sounds good, let's talk next week. Does Tuesday 10:00 work?",
"body": "Sounds good, let's talk next week. Does Tuesday 10:00 work?\n\nAda"
}
],
"next_cursor": null
}
The fields are those of Replies plus body, the full text of the message. Reading the list does not mark a message as read.
Errors: 400 for an invalid category, unread, since, UUID, limit or cursor.
Answer a message#
POST /api/v1/inbox/messages/{id}/reply sends an answer from the mailbox the message arrived in, in the same thread, to the sender of the message. It runs the Reply action of the inbox in the app. This sends real email.
Body (JSON, up to 64 KiB)
| Field | Type | Required | Notes |
|---|---|---|---|
body |
string | Yes | Plain text, up to 20,000 characters after trimming. |
curl -X POST "https://app.anymail.watch/api/v1/inbox/messages/20000000-0000-0000-0000-000000000001/reply" \
-H "Authorization: Bearer $ANYMAIL_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 1d7c2f4e-8a90-4b3c-9d1e-2f3a4b5c6d7e" \
-d '{"body":"Thanks Ada, Tuesday 10:00 works. I will send an invite."}'
const res = await fetch("https://app.anymail.watch/api/v1/inbox/messages/20000000-0000-0000-0000-000000000001/reply", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYMAIL_TOKEN}`,
"Content-Type": "application/json",
"Idempotency-Key": "1d7c2f4e-8a90-4b3c-9d1e-2f3a4b5c6d7e",
},
body: JSON.stringify({"body": "Thanks Ada, Tuesday 10:00 works. I will send an invite."}),
});
console.log(res.status, await res.text());
import os
import requests
response = requests.request(
"POST",
"https://app.anymail.watch/api/v1/inbox/messages/20000000-0000-0000-0000-000000000001/reply",
headers={
"Authorization": f"Bearer {os.environ['ANYMAIL_TOKEN']}",
"Idempotency-Key": "1d7c2f4e-8a90-4b3c-9d1e-2f3a4b5c6d7e",
},
json={'body': 'Thanks Ada, Tuesday 10:00 works. I will send an invite.'},
)
print(response.status_code, response.text)
Response 200
{ "sent": true, "to": "ada@example.com" }
When the message went out but anymail could not record it, the response is still 200, with warning in place of to. Do not retry in that case, because a retry sends a second email. An Idempotency-Key protects a retry after a timeout.
Errors
| Status | Message |
|---|---|
400 |
`body` must be a non-empty string., `body` is limited to 20000 characters. |
404 |
Message not found. This includes a message from another workspace and an id that is not a UUID. |
409 |
The mailbox this reply arrived in no longer exists. or This reply has no sender address to answer. |
502 |
Couldn't send: <reason>. The mail server refused the message. |