API Reference
Send Email
The single endpoint behind all email delivery. It works identically for BYOC and Shared SES: the routing decision is made automatically from your sender domain.
Endpoint: POSThttps://api.sendfleet.net/v1/send
Request headers
| Header | Value | Required |
|---|---|---|
| X-API-Key | Your API key | Yes |
| Content-Type | application/json | Yes |
There is no SDK and no client library. Anything that can send an HTTP request with a JSON body can send email, and the examples below cover cURL, Python, Node.js, PHP, Go and Ruby. On WordPress, the official plugin makes this call for every wp_mail() email, so there is no code to write.
Example request
curl -X POST https://api.sendfleet.net/v1/send \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "alice@example.com",
"from_email": "hello@yourdomain.com",
"from_name": "Your App",
"subject": "Your order has shipped",
"message": "Hi Alice,\n\nYour order #1234 has shipped!\n\nTrack it: https://track.example.com/1234\n\nThanks,\nYour App",
"html": "<p>Hi Alice,</p><p>Your order <strong>#1234</strong> has shipped!</p><p><a href=\"https://track.example.com/1234\">Track your order</a></p>",
"reply_to": "support@yourdomain.com",
"attachments": [
{
"filename": "invoice.pdf",
"content": "JVBERi0xLjQK...",
"content_type": "application/pdf"
}
]
}'The smallest body that works
A recipient, a sender and one body. Every other field on this page is optional and can be added later.
{
"to": "user@example.com",
"from_email": "hello@yourdomain.com",
"message": "Thanks for your order!"
}Request body
All parameters are passed as a JSON object in the request body.
Several recipients
One request sends one message to everyone in to, cc and bcc. Each recipient counts as one send against your monthly allowance, the way Amazon SES counts them, so the example below uses four. A send that needs more than your plan has left is refused whole with 429 usage_limit_exceeded; it never goes to only some of its recipients.
{
"to": ["alice@example.com", "bob@example.com"],
"cc": "accounts@example.com",
"bcc": ["archive@yourdomain.com"],
"from_email": "orders@yourdomain.com",
"subject": "Your order #1042",
"message": "Thanks for your order!"
}| Rule | Detail |
|---|---|
| At most 50 recipients | Across to, cc and bcc together. More is a 400. |
| Shared SES: exactly one address in to | Put one address in to, as a string or a one-item list, and up to four others in cc or bcc. More than one address in to, or more than 5 recipients in total, returns 403 recipient_limit_exceeded. |
| No address twice | Compared case-insensitively across all three lists. A repeat is a 400. |
| Shared SES: one log entry and one event per recipient | The dashboard's email log lists each recipient separately, and every webhook concerns one recipient, named in recipient, under the send's single idempotency_key. |
| Free plan smoke test: no cc or bcc | A Shared SES smoke test reaches only your own verified address, so to must be exactly that address and cc and bcc are refused with 403 unauthorized. Free BYOC sending supports both. |
| Test addresses go alone | delivered@, bounce@ and complaint@ at sendfleet.net must be a send's only recipient. |
Successful response
HTTP 200 OK. The email has been accepted and queued, delivery is asynchronous, so this is not a delivery confirmation.
{
"success": true,
"message": "Email queued for delivery via your SES account.",
"message_id": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"idempotency_key": "3c59dc048e8850243be8079a5c74d079",
"mode": "byoc",
"attachments": 2
}{
"success": true,
"message": "Email queued for delivery.",
"message_id": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
"idempotency_key": "8f14e45fceea167a5a36dedd4bea2543",
"mode": "shared",
"warnings": [
"DMARC record not found. Add 'v=DMARC1; p=none;' as a TXT record on _dmarc.yourdomain.com to improve inbox placement."
]
}Response fields
| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on HTTP 200. |
| message | string | Human-readable confirmation. Wording differs between modes; do not branch on it. |
| message_id | string | An opaque reference for this accepted request. Useful when contacting support about a specific send. It does not appear on any webhook — use idempotency_key for correlation. |
| idempotency_key | string | Shared SES, and BYOC with delivery tracking on. The correlation key for this send. The same value appears on every webhook and email log entry for it, so this is what you store against your own records. Absent on an untracked BYOC send. |
| mode | string | "byoc" or "shared", the sending mode selected for this request. This does not confirm delivery. |
| attachments | number | Count of attachments accepted. Present only when the request included attachments. |
| recipients | number | Number of recipients across to, cc and bcc, each counted as one send. Present only when there is more than one. |
| warnings | string[] | Optional. Non-blocking deliverability issues such as a missing MAIL FROM or DMARC record. Shared SES only. |
Error responses
Sending refusals use the shape below. error is a machine-readable code to branch on;detail is a human-readable explanation. Field validation errors return a map of field names to messages. Authentication, rate-limit and HTTP-level errors can return only detail.
{
"success": false,
"error": "domain_not_verified",
"detail": "Domain 'yourdomain.com' is not yet verified. Add both TXT ownership records, then press Verify all records at https://sendfleet.net/dashboard/domains/"
}| HTTP | error | Cause |
|---|---|---|
| 400 | (field errors) | Missing or invalid body fields, including malformed attachments, more than 50 recipients, or a repeated recipient. The body is a map of field name to error messages, not the standard shape above. |
| 403 | — (no `error` field) | Missing, invalid, expired, or revoked API key. |
| 403 | unauthorized | Account email unverified, subscription inactive for the required mode, sending unavailable, BYOC not yet connected or SendFleet can no longer access your AWS account, a SendFleet test address sent from a BYOC domain, or a Free plan Shared smoke test addressed to anyone but your own verified address. |
| 403 | domain_not_registered | The from_email domain is not in your account. |
| 403 | domain_not_verified | Domain ownership has not been confirmed. |
| 403 | dkim_not_verified | The three DKIM CNAME records have not been confirmed. |
| 403 | domain_mode_mismatch | The domain's sending mode does not match your current plan. |
| 403 | domain_not_allowed | The API key is scoped to specific domains and the sender domain is not among them. |
| 403 | recipient_limit_exceeded | The message is shaped or sized in a way this account may not send on the path it took. The rule is set by the sending path: 50 across to, cc and bcc on BYOC; on Shared SES, exactly one address in to and 5 in total. |
| 403 | sending_suspended | Account sending is temporarily paused or suspended, BYOC sending is paused by the reputation protection you chose, or Shared sending is being re-enabled after a plan change. Check the detail and your dashboard. |
| 429 | usage_limit_exceeded | Monthly email quota exhausted for your plan, your own BYOC monthly cap reached, or fewer sends left than the request has recipients. |
| 429 | — | Per-key rate limit exceeded (60 requests per minute). |
| 502 | attachment_upload_failed | Attachments could not be staged for delivery. Retry with backoff. |
| 502 | queue_error | The message could not be accepted for delivery. Safe to retry with backoff. |
| 503 | byoc_not_configured | A BYOC configuration problem on our side. Contact support. |
| 500 | internal_error | Unexpected error on our side. Retry once, then alert. |
See the full Error Codes reference for retry guidance and worked handling examples.
HTML email tips
Email HTML is not browser HTML. Gmail, Outlook, and Apple Mail strip modern CSS and block external resources, so the rules are stricter than you are used to:
| Rule | Why |
|---|---|
| Use inline CSS only | <style> blocks are stripped by Gmail and many mobile clients. |
| Use table-based layouts | Flexbox and Grid are ignored by Outlook, which renders with the Word engine. |
| Host images on absolute HTTPS URLs | Many clients block external images by default. Always set meaningful alt text so a blocked image still communicates. |
| Always include a plain-text body | It feeds spam scoring and is what assistive technology and text-only clients read. |
| Keep the HTML under 100 KB | Gmail clips messages past roughly 102 KB, hiding whatever is at the bottom — usually your unsubscribe footer. |
| Test across clients before a large send | Rendering varies wildly. Litmus and Email on Acid both cover the major clients. |