Mail Ingest API
Add inbox mail without IMAP. Look and correct API-added rows only. No send.
The Mail Ingest API writes into the same mail_messages table as the IMAP worker, so scraped mail shows up on koveh.com/mail. Use it when IMAP is unavailable or paid.
Base URL
https://api.koveh.com/mail/
Authentication
Authorization: Bearer <api_key> or X-API-Key: <api_key>
api_users.allowed_models must include mail, mail-ingest, or mail-inbox. Admin keys also work.
This API cannot send mail. It can only look, add, and correct rows that were added this way (ingest_source = api). IMAP rows stay read-only.
Endpoints
Health
GET /health — no auth
Accounts
GET /accounts — mailboxes the scraper may file into
List
GET /messages?account=&ingest_source=&since=&q=&limit=&offset=
One message
GET /messages/{id}
Add
POST /messages
Accepts one object, an array, or { "messages": [...] }.
JSON body:
{
"account": "daniil@koveh.com",
"subject": "Concert Friday",
"sender": "Tickets <hello@example.com>",
"recipient": "daniil@koveh.com",
"received_at": "2026-08-19T18:00:00Z",
"body_text": "Doors at 19:00. Hall 2."
}If message_id is missing, the API stores a stable api:<sha256> id so the same scrape does not duplicate.
Correct
PATCH /messages/{id} — only when ingest_source is api. IMAP mail returns 409.
Provenance
| Column | Meaning |
|---|---|
ingest_source | imap or api |
ingested_at | when the row was first written |
ingested_by | API user email that added it |
Example
curl -X POST "https://api.koveh.com/mail/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account": "daniil@koveh.com",
"subject": "Concert Friday",
"sender": "Tickets <hello@example.com>",
"received_at": "2026-08-19T18:00:00Z",
"body_text": "Doors at 19:00."
}'