Skip to content
Mails · API reference

Flaxxa Mails email API and webhooks.

Drive Flaxxa Mails from your own software: a REST API with workspace keys, signed webhooks and idempotency keys.

No credit card
Free plan
Cancel anytime
flaxxa.com / product / mails / docs
cURLREST · JSON
# Flaxxa Mails · first request
curl https://flaxxa.com/api/v1/mails \
  -H "Authorization: Bearer $FLAXXA_KEY" \
  -H "Accept: application/json"

# webhooks: X-Flaxxa-Signature (HMAC-SHA256)
# retries: Idempotency-Key, 24 h
200 OKapplication/json
+ webhooks · HMAC-signed
REST
Requests per key by default
120/min
Idempotency window
24h
Quick start

How do I make my first API call?

Replace $FLAXXA_KEY with a key from your workspace. The first call returns a real id you can use for the next ones.

cURL
curl -X POST https://flaxxa.com/api/v1/mails/send \
  -H "Authorization: Bearer flx_live_YOUR_KEY" \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{
    "account_id": 12,
    "to":       [{ "email": "[email protected]", "name": "Asha Verma" }],
    "subject":  "Your proposal is ready",
    "body_html": "<p>Hi Asha, the proposal is attached.</p>",
    "attachments": [{ "filename": "Proposal.pdf", "mime": "application/pdf", "size_bytes": 48211,
                      "storage_disk": "r2", "storage_key": "mails/…/outbound/…-Proposal.pdf" }]
  }'
PHP
$h = Http::withToken(env('FLAXXA_MAILS_KEY'))->acceptJson();

// 1. upload the attachment (multipart), 2. send from the connected account
$file = $h->attach('file', file_get_contents($pdfPath), 'Proposal.pdf')
          ->post('https://flaxxa.com/api/v1/mails/attachments')->throw()->json('data');

$sent = $h->post('https://flaxxa.com/api/v1/mails/send', [
    'account_id' => 12,
    'to'         => [['email' => $customer->email, 'name' => $customer->name]],
    'subject'    => "Proposal #{$proposal->id}",
    'body_html'  => view('mail.proposal', compact('proposal'))->render(),
    'attachments' => [$file],
])->throw()->json('data');   // $sent['id'], $sent['status'] === 'pending' (undo window), then 'sent'
Node
const res = await fetch("https://flaxxa.com/api/v1/mails/send", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.FLAXXA_MAILS_KEY}`, Accept: "application/json", "Content-Type": "application/json" },
  body: JSON.stringify({
    account_id: 12,
    to: [{ email: "[email protected]" }],
    subject: "Your proposal is ready",
    body_text: "Hi Asha, the proposal is attached.",
    scheduled_for: "2026-11-01T09:00:00+05:30",   // optional: send later
  }),
});
const { data } = await res.json(); // data.id, data.status
Python
import requests
r = requests.post("https://flaxxa.com/api/v1/mails/send",
    headers={"Authorization": f"Bearer {KEY}", "Accept": "application/json"},
    json={"account_id": 12, "to": [{"email": "[email protected]"}],
          "subject": "Your proposal is ready", "body_html": "<p>Hi Asha…</p>"})
r.raise_for_status()
queued = r.json()["data"]   # id, status, undo_window_seconds
Request
POST /api/v1/mails/send HTTP/1.1
Host: flaxxa.com
Authorization: Bearer flx_live_·············
Content-Type: application/json
Idempotency-Key: proposal-42-mail

{
  "account_id": 12,
  "to":        [{ "email": "[email protected]", "name": "Asha Verma" }],
  "cc":        [{ "email": "[email protected]" }],
  "subject":   "Your proposal is ready",
  "body_html": "<p>Hi Asha, the proposal is attached.</p>",
  "attachments": [{ "filename": "Proposal.pdf", "mime": "application/pdf", "size_bytes": 48211,
                    "storage_disk": "r2", "storage_key": "mails/…/outbound/…-Proposal.pdf" }]
}
Response · 201
HTTP/1.1 201 Created
Content-Type: application/json

{
  "data": {
    "id": 9041,
    "status": "pending",
    "account_id": 12,
    "subject": "Your proposal is ready",
    "to": [{ "email": "[email protected]", "name": "Asha Verma" }],
    "scheduled_for": null,
    "undo_window_seconds": 10
  }
}
Authentication

How do I authenticate?

Every call requires Authorization: Bearer <key>. Rotate from workspace settings — old keys are revoked instantly. Server-side only; never ship a live key to the browser.

Base URLhttps://flaxxa.com/api/v1/mails
Send mail

Send mail API

Send from a connected account (Gmail or IMAP/SMTP). A send is queued, held for the workspace's undo window, then delivered; `scheduled_for` sends later. The key must belong to the account's owner, or carry mails.shared_inbox.reply.

POST/send

Send a message

Body: account_id, to[] ({email,name}), cc[], bcc[], subject, body_html and/or body_text, attachments[] (from /attachments), in_reply_to_message_id, references_chain[], scheduled_for (ISO date, future). Returns the queue row with its status and undo window.

POST/send/{id}/cancel

Recall

Cancels a message still in the undo window or scheduled; { cancelled: true|false, status }.

POST/attachments

Upload attachment

multipart/form-data `file` (≤25 MB). Returns { filename, mime, size_bytes, storage_disk, storage_key } — pass that object in attachments[] of /send.

GET/threads/{id}/reply-draft

Reply draft

Pre-filled to/cc/subject/body for a reply (`?reply_all=1` includes everyone). POST the result to /send.

GET/messages/{id}/forward-draft

Forward draft

Pre-filled subject/body/attachments for forwarding a message.

Drafts

Drafts API

Server-side drafts (what the composer autosaves). Optional for integrations that assemble a message before sending.

GET/drafts

List drafts

The caller's drafts.

POST/drafts

Create draft

Same fields as /send; nothing is sent.

PUT/drafts/{id}

Update draft

Replace fields.

DELETE/drafts/{id}

Delete draft

Discard.

Threads & messages

Threads & messages API

Read the caller's mailboxes. Threads group messages across folders; bodies are returned per message.

GET/threads

List threads

Filters: account_id, folder_role (inbox|sent|drafts|trash|spam|archive), filter (unread|starred|snoozed), label_id, q (subject/sender/body search), per_page (≤200). Returns { data[], total, current_page, … }.

GET/threads/{id}

Thread

{ thread, messages[] } with bodies and attachment metadata.

GET/messages/{id}

Message

One message with body, headers, from/to/cc, flags and attachments (each with download_url).

GET/attachments/{id}

Download attachment

The attachment bytes.

GET/counts

Unread counts

Unread per folder/account for badges.

POST/threads/mark-read

Mark read/unread

{ thread_ids[], is_read } — mirrored to the provider.

POST/threads/{id}/star · /pin · /snooze

Flag a thread

Toggle star / pin; snooze takes { until }.

POST/threads/move

Move threads

{ thread_ids[], target_role } — archive, trash, spam or inbox.

POST/threads/destroy

Delete for good

{ thread_ids[] } — permanently deletes conversations that are already in Trash.

POST/messages/{id}/send-to-sign

Send PDF to Sign

Pushes a PDF attachment into Flaxxa Sign as a new envelope (Sign must be installed).

Labels

Labels API

GET/labels

List labels

Workspace and account labels with colours.

POST/labels

Create label

{ name, color, scope (workspace|account), account_id } — scope mails.label.manage.

DELETE/labels/{id}

Delete label

Removes the label from every thread.

POST/threads/apply-label

Apply / remove

{ thread_ids[], label_id, attach: true|false }.

Accounts

Accounts API

Connected mailboxes. Gmail connects through Google sign-in in the app; IMAP/SMTP accounts can be created over the API.

GET/accounts

List accounts

The caller's connected accounts — the account_id you send from.

POST/accounts/test-connect

Test IMAP

{ imap_host, imap_port, imap_encryption, imap_username, password } — checks the IMAP login without saving.

POST/accounts/probe-smtp

Probe SMTP

{ smtp_host, username, password } — tries the common SMTP port and encryption combinations and returns the first that authenticates.

POST/accounts/imap

Connect IMAP/SMTP

{ display_name, email_address, imap_host, imap_port, imap_encryption, imap_username, smtp_host, smtp_port, smtp_encryption, smtp_username, password, is_default }.

PUT/accounts/{id}

Update account

display_name, sync_paused, sync_window_months (1–6), sync_max_messages (up to the plan limit).

PUT/accounts/{id}/signature

Signature

{ signature_html } appended to messages sent from the account.

POST/accounts/{id}/sync

Sync now

Queues an immediate fetch.

POST/accounts/{id}/default

Make default

The account the composer picks first.

DELETE/accounts/{id}

Disconnect

Removes the account and its local copy of mail.

Webhooks

Webhooks API

POST /api/v1/webhooks { url, subscribed_events[] } (scope workspace.webhooks.manage). Deliveries carry X-Flaxxa-Event and X-Flaxxa-Signature (HMAC-SHA256 of the raw body with the endpoint secret, shown once).

POST/../webhooks

Subscribe

Register your endpoint for app.mails.* events.

DELETE/../webhooks/{id}

Unsubscribe

Stops delivery.

Webhook events

Which webhook events can I subscribe to?

Subscribe via POST /webhooks. Verify X-Flaxxa-Signature on every payload.

  • app.mails.message.sent

    A message sent through /send actually left the account — queue_id, account, from, to[], subject, smtp_message_id, sent_at.

  • app.mails.message.received

    New mail arrived in a connected inbox (after the account was connected) — message_id, thread_id, account, from, to[], subject, snippet, received_at.

Errors

What errors can the API return?

All errors return a JSON envelope: { error: { code, message, details } }. Retry idempotent calls on 5xx with exponential backoff.

CodeNameWhen it fires
401unauthenticatedAPI key missing, expired or revoked.
403insufficient_scopeKey valid but lacks the scope — mails.send to send, mails.account.connect to read mail and accounts, mails.label.manage for labels.
403not_your_accountThe account_id belongs to another member; sending from it needs mails.shared_inbox.reply.
404not_foundNo thread / message / account with that id in this workspace.
422validation_failed{ message, errors } — e.g. to[] empty, subject missing, attachment object incomplete, scheduled_for in the past.
429rate_limited120 requests/minute per key by default.
Conventions

Which rules apply to every request?

Every Flaxxa app's API follows the same authentication, rate limits, idempotency and webhook signing.

Bearer auth

Every API call uses a Bearer token. Keys belong to a workspace and are scoped per app. Create and revoke them from workspace API settings.

Rate limits

Each key can make 120 requests a minute unless the app sets its own limit. Going over returns 429 with a Retry-After header.

Idempotency

Send an Idempotency-Key header on a POST and a retry with the same key within 24 hours returns the first response instead of running again.

Signed webhooks

Every webhook payload carries an X-Flaxxa-Signature HMAC-SHA256 header made with the secret you get when you subscribe. Verify it before processing.

API FAQ

Flaxxa Mails API: common questions

Frequently asked questions

Yes. Connect the Gmail account in Flaxxa Mails, then POST /send with an API key that has the mails.send scope and the account_id. The message goes out through Gmail from your real address.

Get started in minutes

Ship faster with Flaxxa Mails.

Create a workspace, make an API key in its settings, and send your first request.

Free plan · No credit card · Cancel anytime