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> — see Authentication.
- A project key always lists its own project;
project_idis optional and, if present, must match that project. - An org/scoped key with no
project_idlists across every project the key can see. Pass?project_id=to narrow to one.
Query parameters
| Parameter | Required | Notes |
|---|---|---|
project_id | No | Must be a UUID. Required narrowing for an org key that should only see one project; optional confirmation for a project key. |
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. |
| 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_…"