Common tasks

Read a conversation, search, mark as read, send, prepare a draft for review, reply in a thread and let Maylee classify email, with copyable requests.

Every example assumes a key in $MAYLEE_KEY (see Authentication) and a mailbox id from GET /mailboxes.

Read an email and its conversation

A list gives metadata only: the body costs a round trip to storage, so it is fetched on demand.

curl https://api.maylee.app/api/v1/emails/cmemx1a2b3c4d5e6f7g8h9i0/body \
  -H "Authorization: Bearer $MAYLEE_KEY"
{
  "emailId": "cmemx1a2b3c4d5e6f7g8h9i0",
  "html": "<p>Hello, could you send us a quote for...</p>",
  "text": "Hello, could you send us a quote for...",
  "thread": [
    {
      "id": "cmemx1a2b3c4d5e6f7g8h9i0",
      "messageId": "<CAF3k9x1@mail.example.com>",
      "references": [],
      "fromAddress": "client@acme.com",
      "fromName": "Jane Client",
      "toAddresses": [{ "email": "contact@your-company.com" }],
      "date": "2026-08-25T13:47:02.000Z",
      "subject": "Quote request",
      "html": "<p>Hello, could you send us a quote for...</p>",
      "text": "Hello, could you send us a quote for...",
      "isMe": false
    }
  ]
}

html and text are the body of the email you asked for. thread holds every message of the conversation, oldest first, including your own replies (isMe: true) and any draft in progress.

curl -G https://api.maylee.app/api/v1/emails/search \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  --data-urlencode 'q=quote'

The term (2 to 200 characters) is matched as one piece of text, ignoring case, against the subject, the sender name and address, the preview snippet and the To recipients. Full bodies and Cc recipients are not searched. Results are ranked by relevance, subject matches first.

The search spans all folders: whoever looks for an invoice does not know whether it is still in the inbox. Add folderType=SENT to narrow it down. For structured criteria (sender, labels, dates, read state), use filters on GET /emails.

Mark as read

curl -X PATCH https://api.maylee.app/api/v1/emails/cmemx1a2b3c4d5e6f7g8h9i0 \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"isRead": true}'

The change is pushed in real time to open Maylee windows and propagated to the provider in the background. To process many emails at once, use POST /emails/bulk (up to 500 ids, one quota unit per email).

Send an email

Sending needs the write scope and the Send emails permission of the key. With the scope alone you get 403 OUTBOUND_SEND_NOT_ALLOWED.

curl -X POST https://api.maylee.app/api/v1/drafts/send \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "mailboxId": "cmbx1a2b3c4d5e6f7g8h9i0j",
    "to": [{ "email": "client@acme.com", "name": "Jane Client" }],
    "subject": "Your quote",
    "body": { "text": "Hello,\n\nPlease find the requested quote below." }
  }'
{
  "messageId": "<20260825140233.1a2b3c@your-company.com>",
  "threadId": "18c2f5a9e7b1d3c4"
}

Always send an Idempotency-Key

A network timeout does not mean the send failed. Without the key, a retry delivers the message a second time. See Idempotency.

Attachments cannot be sent through the API yet.

Prepare a draft for a person to review

When a person should check the email before it goes out, create a draft instead. It takes the same fields, and the write scope is enough: nothing leaves the workspace.

curl -X POST https://api.maylee.app/api/v1/drafts \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "mailboxId": "cmbx1a2b3c4d5e6f7g8h9i0j",
    "to": [{ "email": "client@acme.com", "name": "Jane Client" }],
    "subject": "Your quote",
    "body": { "text": "Hello,\n\nPlease find the requested quote below." }
  }'

The draft appears at once in the Drafts folder of the Maylee app, where the user reviews it, edits it and sends it. The response is 201 with the draft itself (isDraft: true). The threading fields below work the same way on drafts.

Reply in a thread

A reply is a send with three extra fields taken from the original email, so that every mail client groups it in the same conversation. First read the original with GET /emails/{emailId} and keep its mailboxId, messageId, references, from and subject. Then:

curl -X POST https://api.maylee.app/api/v1/drafts/send \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "mailboxId": "cmbx1a2b3c4d5e6f7g8h9i0j",
    "to": [{ "email": "client@acme.com", "name": "Jane Client" }],
    "subject": "Re: Quote request",
    "inReplyTo": "<CAF3k9x1@mail.example.com>",
    "references": ["<CAF3k9x1@mail.example.com>"],
    "body": { "text": "Hello Jane,\n\nHere is the quote you asked for." }
  }'

The rules:

  • inReplyTo is the messageId of the email you answer.
  • references is the original's references array plus its messageId, in that order. On a first reply the original has no references, so the array holds the single messageId.
  • Reply to the from of the original, and prefix the subject with Re: unless it already starts with it.
  • Send from the mailboxId the original arrived in.

The response's threadId is the provider's conversation id when the provider exposes one. Maylee's own thread view relies on the headers above and works either way.

Let Maylee classify email

Everything you configure by hand in the app can be configured through the API. Two calls set up an AI label and a view that shows it:

curl -X POST https://api.maylee.app/api/v1/ai-label-rules \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Prospecting",
    "color": "Purple",
    "condition": "An unsolicited sales or partnership pitch from someone we have never worked with."
  }'

color is one of Pink, Blue, Red, Yellow, Green, Purple or Orange: these are the names the app renders. Keep rule.label.id from the response, then create a view on it:

curl -X POST https://api.maylee.app/api/v1/views \
  -H "Authorization: Bearer $MAYLEE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Prospecting",
    "icon": "📣",
    "filters": { "id": "c1", "label": "ai_label", "type": "contains", "value": "<rule.label.id>" }
  }'

From now on, every matching email is labelled when it arrives and grouped in the view. Rules apply to emails received after their creation: to classify the emails already there, call POST /ai-label-rules/backfill once. Add "hideFromInbox": true to the rule and those emails leave the main inbox, still reachable in the view and with GET /emails?filtered=only.

What next

  • Branch on code, never on error. Messages are written for humans and may be reworded; codes are a contract. See Errors.
  • Read the quota headers. X-RateLimit-Remaining tells you when to slow down before you hit a 429. See Rate limits and quotas.
  • Webhooks are coming soon. Until then, poll with a cursor at a reasonable interval rather than in a tight loop. See Webhooks.

On this page