Email inbox endpoints

Backed by the Emails model. See Email inbox and replies.

GET /api/emails

page (default 1), page_size (default 25, max 100).

Parameter Filter

direction

all (default) or an EmailDirection value

inbox_category

all (default) or an EmailInboxCategory value

search

Free text over subject, body and addresses

include_archived

true or 1 to include archived messages

sending_domain

all, none (no sendingDomainId), or an account UUID

campaign_id

Restrict to one campaign

target_category

all or an EmailTargetCategory value

Each row:

{
  "id": "uuid",
  "direction": "received",
  "inbox_category": "reply",
  "thread_id": "uuid",
  "external_id": "resend_email_id",
  "to_email": "alex@payability.com",
  "from_email": "prospect@example.com",
  "subject": "Re: Quick question",
  "body": "plain text",
  "body_html": "<p>…</p>",
  "opened": false,
  "read_at": null,
  "archived_at": null,
  "sent_at": null,
  "created_at": "2026-01-01T00:00:00.000Z",
  "campaign_id": "uuid",
  "campaign_name": "Campaign Name",
  "contact_id": "uuid",
  "contact_name": "John Doe",
  "target_category": "interested",
  "reply_count": 2,
  "is_read": false
}

opened is the recipient’s tracking pixel; is_read (derived from read_at) is whether an operator has read it. reply_count is the number of messages in the thread.

GET /api/emails/{id}

The full thread, ordered oldest first. Each message returns id, direction, from_email, to_email, subject, body, body_html, sending_domain_id, sent_at, created_at.

PATCH /api/emails/{id}

Updates in-app state — read, archived and category.

POST /api/emails

Sends a one-off email, used for manual replies. Validated by PostEmailSchema.

Effects:

  • Sends through Resend with a Reply-To derived from the sending account (buildReplyToForSendingDomain).

  • Writes an Emails row with direction = sent, reusing the thread’s threadId when replying.

  • Writes an Activity row.

  • Converts the HTML body to plain text with htmlToPlainText so the message is searchable.