List emails
GET /api/v1/emails — read a project's mailbox. No pagination, no single-message GET.
GET /api/v1/emails
Lists mail in a project's mailbox, newest first. This is the only route
under /api/v1/emails — there is no POST, PATCH, or DELETE here, and
no /api/v1/emails/{id} for a single message. Reading one message by id is
an MCP-only capability: the get_email tool on the MCP page.
Auth
Authorization: Bearer <key> with inbox ≥ read — see
Authentication.
- With no
project_id, the response covers every project in the key's scope — one project for a key scoped to just that one, more for a key scoped to several projects or to all of them. - Pass
?project_id=to narrow to a single project; it must be one the key is authorized for.
Query parameters
| Parameter | Required | Notes |
|---|---|---|
project_id | No | Must be a UUID and inside the key's project scope. Narrows the listing to one project; omit it to list everything the key can see. |
Response
200 OK, Cache-Control: private, no-store:
{
"emails": [
{
"id": "email-uuid",
"direction": "inbound",
"from_addr": "ada@example.com",
"to_addr": "you@yourproject.example",
"subject": "Hello",
"body_text": "...",
"body_html": "...",
"status": "received",
"created_at": "2026-09-03T20:00:00.000Z"
}
]
}Wider than the MCP shape
This is a raw select("*") on the emails row — every column the table
has, including ones added later. It is not the same shape as MCP's
list_emails tool, which returns a fixed, hand-picked projection
(summarizeEmail in apps/web/lib/mcp.ts). Don't assume the two match
field-for-field; read whichever response you're actually consuming.
No pagination
There is no limit, offset, cursor, or page parameter, no total,
and no has_more. The response is capped at a hard 50 rows
(MAILBOX_PAGE_SIZE in apps/web/lib/emails.ts) — the 51st message in a
mailbox is simply not in the array, with nothing in the response telling
you it was cut. If you need everything, there is currently no way to page
past the first 50 through this endpoint.
Errors
| Status | Body error | Cause |
|---|---|---|
| 401 | missing or malformed API key | No Authorization header, or it isn't Bearer <key>. |
| 401 | invalid API key | The key doesn't match any stored hash. |
| 401 | API key has been revoked | revoked_at is set. |
| 403 | this key lacks inbox:read | The key doesn't hold inbox ≥ read. |
| 400 | project_id must be a uuid | ?project_id= present but not UUID-shaped. |
| 403 | API key is not authorised for this project | The key's scope doesn't cover the requested project. |
| 500 | lookup failed | The key lookup itself errored. |
Error strings are exact, read from apps/web/app/api/v1/emails/route.ts at
the time this page was written.
curl
curl https://kontacts.dev/api/v1/emails \ -H "Authorization: Bearer gm_live_…"