Documentation

API reference

Everything you need to send your first email and keep it landing in inboxes.

Quickstart

The API is a plain JSON REST interface over HTTPS. Base URL:

base url
https://api.sendflit.com/v1

Three steps to your first send:

  • Create an account and add your domain in the dashboard.
  • Publish the DNS records shown, then verify the domain.
  • Create an API key and call POST /v1/email.
Verification is required. Sending from a domain you have not verified returns 403. This protects you as much as us: unverified sending is how a shared sending reputation gets destroyed.

Authentication

Pass your key as a bearer token on every request.

auth
-H "Authorization: Bearer re_your_api_key"

Keys carry a scope, so a key that only needs to send cannot read your audience:

ScopeCan do
fullEverything, including key and domain management.
sendingSend email and read its own delivery status.
readRead-only. Cannot send.

Keys are shown once at creation and stored only as a SHA-256 hash. Rotate one by creating a replacement and revoking the old key with DELETE /v1/keys/<id> — revocation is immediate.

Domains

POST/v1/domains registers a sending domain and returns the DNS records to publish. Each domain gets its own 2048-bit DKIM keypair, so receivers verify your signature.

add a domain
curl -X POST https://api.sendflit.com/v1/domains \
  -H "Authorization: Bearer $SENDFLIT_KEY" \
  -d '{"name": "yourdomain.com"}'
RecordHostPurpose
TXTsendflit._domainkey.yourdomain.comDKIM — signs your outgoing mail
TXTyourdomain.comSPF — authorises sending for the domain
TXT_dmarc.yourdomain.comDMARC — policy and aggregate reports

Then POST/v1/domains/<id>/verify resolves the records and marks the domain verified. If your DNS is in Route 53, connect AWS credentials once and POST/v1/domains/<id>/configure-dns writes all three records for you, merging into any SPF record you already have rather than replacing it.

Sending email

POST/v1/email sends one message.

request
{
  "from": "hello@yourdomain.com",
  "to": "user@example.com",
  "subject": "Welcome aboard",
  "html": "<p>Thanks for signing up.</p>",
  "reply_to": "support@yourdomain.com",
  "scheduled_at": "2026-09-01T09:00:00Z"
}
FieldTypeNotes
fromstringRequired. Must be a verified domain on your account.
tostringRequired. One recipient.
subjectstringRequired unless supplied by a template.
html / textstringAt least one. Both are recommended.
cc / bccstring[]Optional.
reply_tostringOptional.
headersobjectOptional custom headers.
scheduled_atISO 8601Send later. Cancel with DELETE until it fires.
template_namestringRender a stored template with variables.
variablesobjectValues for {{name}} placeholders.
attachmentsobject[]Up to 10, base64, 10 MB each.

POST/v1/emails/batch takes up to 100 messages in one call and returns a per-message result, so one bad recipient does not fail the batch. GET/v1/emails lists sends with limit, offset, q and status. DELETE/v1/emails/<id> cancels a scheduled send.

Templates

Store reusable HTML with {{variable}} placeholders and render at send time by name. Subject lines are rendered too.

send with a template
{
  "from": "hello@yourdomain.com",
  "to": "user@example.com",
  "template_name": "welcome",
  "variables": {"name": "Ada", "plan": "Pro"}
}

Broadcasts & audiences

Contacts belong to an audience. A broadcast sends to every subscribed contact in one, skipping anyone on your suppression list.

Broadcast delivery is asynchronous: the call returns once the messages are queued, and the worker drains them. Poll GET/v1/broadcasts/<id> for progress — the status moves from sending to sent when the last message leaves.

import contacts
curl -X POST https://api.sendflit.com/v1/contacts/import \
  -H "Authorization: Bearer $SENDFLIT_KEY" \
  --data-binary $'email,name,audience\\nada@example.com,Ada,default'

Tracking

When a message has an HTML body we append a 1×1 open pixel and rewrite links through a signed redirect. Both are bound to the message id and HMAC-signed, so the redirect cannot be repointed at somebody else's URL.

Bulk mail also carries List-Unsubscribe and List-Unsubscribe-Post headers, which is what Gmail and Yahoo require of bulk senders. The unsubscribe link opens a confirmation page; only a POST records the unsubscribe, so link scanners cannot unsubscribe your recipients by crawling.

Suppressions

A suppressed address is refused at send time with 403. Addresses are added automatically on an unsubscribe, a permanent bounce, or a spam complaint. Transient bounces — a full mailbox, a throttled receiver — do not suppress.

EndpointPurpose
GET/v1/suppressionsList suppressed addresses.
POST/v1/suppressionsAdd one manually.
DELETE/v1/suppressions?email=Remove one.

Webhooks

Register an endpoint and we POST every event to it. Each request carries an X-Sendflit-Signature: sha256=<hex> header — an HMAC of the raw body using your webhook secret. Always verify it, and compare in constant time.

verify.py
import hashlib, hmac

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", header)
EventFires when
email.sentAccepted by the sending infrastructure.
email.deliveredThe receiving server accepted it.
email.openedThe tracking pixel loaded.
email.clickedA tracked link was followed.
email.bouncedDelivery failed permanently.
email.complainedMarked as spam by the recipient.
email.unsubscribedThe recipient used the unsubscribe link.
email.failedWe could not hand it off.

MCP server

SendFlit speaks the Model Context Protocol at /mcp, so an AI agent can use your account directly. Authenticate with a normal API key — the agent inherits that key's scope, rate limit and quota, so a sending key cannot be talked into reading your audience.

claude_desktop_config.json
{
  "mcpServers": {
    "sendflit": {
      "url": "https://api.sendflit.com/mcp",
      "headers": { "Authorization": "Bearer re_your_api_key" }
    }
  }
}

Tools available: send_email, schedule_email, list_emails, get_metrics, add_contact, create_broadcast, create_template, add_domain, and the AI helpers for composing drafts, generating subject variants, summarising metrics, and building segments and automations from a description.

Errors & limits

Errors are JSON with a detail field. Every response carries an X-Request-Id — quote it if you contact support.

StatusMeaning
400Malformed request, or an unsigned tracking link.
401Missing, invalid or revoked credentials.
402Requires a paid plan — start a checkout.
403Suppressed recipient, unverified domain, or insufficient key scope.
404Not found, or not yours.
409Conflict — the resource already exists.
422Validation failed. The message says which field.
429Rate limit, daily limit or monthly quota reached.
502The upstream mail provider rejected the handoff.

Rate limits are per API key, per second, and vary by plan. Monthly and daily send quotas are enforced per account; a broadcast is charged for its whole audience up front, so it either fits or is refused before any mail goes out.