Overview
The API sends email through Cyphex. You verify a domain you own, create a sender address on it and an API key, and your software posts a JSON message to one endpoint. Mail is signed for your domain, so recipients see your brand, not ours.
| Base URL | https://api.cyphex.tech |
| Endpoint | POST /v1/emails |
| Format | JSON in, JSON out, UTF-8 |
| Authentication | Bearer API key (cm_live_...) |
| Server side only | Never put a key in a browser page or a mobile app |
Quickstart
- Get access. Apply for an API plan. We review every application, usually within one business day.
- Add your domain in your dashboard and publish the DNS records we show you (see Domains and senders). Wait until it shows as verified.
- Create a sender address on that domain, for example
orders@yourdomain.com. - Create an API key under Developers. It is shown once: store it as a secret, for example the environment variable
CYPHEX_API_KEY. - Send a test to your own address:
curl -sS -X POST https://api.cyphex.tech/v1/emails \
-H "Authorization: Bearer $CYPHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Shop <orders@yourdomain.com>",
"to": "buyer@example.com",
"subject": "Order 1042 confirmed",
"html": "<p>Thanks for your order!</p>",
"text": "Thanks for your order!"
}'A successful call answers 200 with { "id": "<message id>" }. Open the message in the failed-send log later if you ever need to trace it.
Authentication
Send your key in the Authorization header on every request:
Authorization: Bearer cm_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json- Keys start with
cm_live_. They are stored only as a hash on our side, so we cannot show a key again. Lost key: create a new one and revoke the old one. - One key belongs to one account. It can only send as that account's own sender addresses.
- Use one key per project and environment (for example
shop-prodandshop-staging) so you can revoke one without touching the others. - Rotate by creating the new key, deploying it, then revoking the old one. Revoking is immediate.
- The number of keys depends on your plan: 3 on API Start, 5 on API Growth, 10 on API Only and Enterprise.
Send an email
POST /v1/emails accepts one message per request. Put several people in to, cc or bcc to send the same message to all of them (up to 50 recipients in total).
| Field | Type | Required | Notes |
|---|---|---|---|
from | string | yes | A sender address of your account, with an optional name: Shop <orders@yourdomain.com> |
to | string | yes | One address, or several in one string separated by commas |
cc | string | no | Visible copy recipients |
bcc | string | no | Hidden recipients: they receive the mail but are never shown in any header |
subject | string | yes | Plain text |
html / text | string | one required | Send both for best delivery: some inboxes prefer plain text |
attachments | array | no | Up to 10 files. Each: filename, mimeType, content (base64), max 10 MB each |
{
"from": "Billing <billing@yourdomain.com>",
"to": "client@example.com, accounts@example.com",
"cc": "manager@example.com",
"bcc": "archive@yourdomain.com",
"subject": "Invoice 1042",
"html": "<p>Your invoice is attached.</p>",
"text": "Your invoice is attached.",
"attachments": [
{ "filename": "invoice-1042.pdf", "mimeType": "application/pdf", "content": "<base64 of the file>" }
]
}Recipients are merged and de-duplicated across to, cc and bcc. The count of distinct recipients is what counts toward your limits.
| Response | Body |
|---|---|
200 | { "id": "<message id>" } |
| 4xx / 5xx | { "error": "Human readable message", "code": "stable_code" } |
Errors
Every error has a code. Match on the code, never on the message text: messages can be reworded, codes stay.
| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_json | The body is not valid JSON, or not a JSON object. | Fix the request. Do not retry unchanged. |
| 400 | invalid_request | A required field is missing: from, to, subject, and html or text. | Fix the request. |
| 400 | invalid_recipients | No valid address in to, or more than 50 recipients in to, cc and bcc together. | Send fewer recipients per request. |
| 400 | too_many_attachments | More than 10 attachments. | Send fewer files, or link to them. |
| 401 | invalid_api_key | The Authorization header is missing or is not a Bearer cm_live_ key. | Check the header. |
| 401 | api_key_not_found | The key does not exist (revoked or mistyped). | Create a new key. |
| 401 | account_deactivated | The user that owns the key is deactivated. | Contact your administrator. |
| 403 | forbidden_role | Only administrators and sender addresses can send with the API. | Create the key from an allowed account. |
| 403 | from_not_allowed | The From address is not one of your sender addresses. | Use a verified sender of your own domain. |
| 403 | plan_limit | Your plan or account status stops this send (for example a suspended account or a plan cap). | Check your plan, or contact us. |
| 413 | attachment_too_large | An attachment is over 10 MB. | Compress it or send a link. |
| 429 | rate_limited_minute | More than 120 requests a minute with this key. | Wait for the Retry-After seconds, then retry. |
| 429 | rate_limited_day | The key's daily email limit was reached. | Wait for Retry-After, or ask for a higher limit. |
| 500 | send_failed | Our mail system could not send it. | Retry with backoff. Contact us if it repeats. |
A 429 always carries a Retry-After header in seconds. Refused and failed sends are recorded for 30 days (no message content) and shown in your dashboard under “Failed or refused sends”, so you can debug without contacting us.
Limits and plans
| Limit | Value |
|---|---|
| Requests per key | 120 per minute |
| Emails per key | 5,000 per day, counted in recipients (to + cc + bcc, de-duplicated) |
| Recipients per request | 50 |
| Attachments per request | 10 files, 10 MB each |
| Plan daily cap | API Start 1,250 a day, API Growth 5,000 a day, API Only by agreement |
| API keys per account | 3 (Start), 5 (Growth), 10 (API Only and Enterprise) |
| Included volume | API Start 25,000 a month, API Growth 100,000 a month |
The per-key limits protect everyone's sending reputation. The plan cap is the stricter of the two for small plans. Need more? Ask us: higher limits are agreed together with your price. See plans and pricing.
Domains and senders
You can only send from a domain you have verified, and only as a sender address that exists in your account. Anything else is refused with 403 from_not_allowed.
- Add the domain in your dashboard. We show the DNS records to publish.
- DKIM: three CNAME records. They sign your mail so it is trusted.
- SPF and custom return path: an MX and a TXT record on a subdomain such as
mail.yourdomain.com, so inboxes show your domain and not ours. - DMARC: a TXT record on
_dmarc.yourdomain.com. Start withp=none, move toquarantineafter a clean week. - Press check. When the domain shows verified, create your sender addresses.
DNS changes can take from a few minutes to a few hours to be visible. Use a separate sender per purpose (orders@, security@, news@): it keeps filtering and reputation clean.
Sending rules
These rules are part of our Acceptable-use policy. They exist so that mail from every customer reaches the inbox.
- Send only to people who agreed to receive your email. No purchased, rented or scraped lists.
- Marketing or bulk mail must carry a working unsubscribe link, and you must honour it quickly.
- Keep hard bounces under 2% and spam complaints under 0.05%. Beyond that, sending is paused automatically and you are asked to review your list.
- Do not send phishing, malware, deceptive or illegal content, or mail that hides who you are.
- Do not share a key between unrelated businesses or resell access to it.
- New domains start slowly: raise your volume over a few weeks, not in one day.
We watch bounce and complaint rates automatically. A pause is lifted once the cause is fixed. Repeated or serious breaches can end the agreement.
Best practices
- Send from a queue, not inside a user's web request. A slow or failed send should never block your page.
- Retry only what can succeed:
429and5xx, with waiting between tries. Never retry a4xxunchanged. - Avoid duplicates. There is no idempotency key yet, so store your own “already sent” flag per business event before retrying.
- Spread bulk sends over time instead of one burst, and stay under the per-minute limit.
- Send both HTML and text, keep the subject honest, and use a real reply address.
- Test with your own inboxes (Gmail and Outlook) and check the headers show
spf=pass dkim=pass dmarc=passfor your domain. - Watch the failed-send log after your first real traffic and set an alert on repeated
403or429.
// Retry only 429 and 5xx. Never retry a 4xx: the request itself is wrong.
async function sendWithRetry(mail, tries = 4) {
for (let i = 0; i < tries; i++) {
try { return await sendMail(mail); }
catch (e) {
if (!e.retryable || i === tries - 1) throw e;
const wait = e.retryAfter ? e.retryAfter * 1000 : 1000 * 4 ** i; // 1 s, 4 s, 16 s
await new Promise((r) => setTimeout(r, wait));
}
}
}Security
- All traffic is HTTPS. Requests over plain HTTP are refused.
- Keep keys in environment variables or a secrets manager. Never commit them to Git or ship them in a browser or mobile app.
- If a key may have leaked, revoke it at once and create a new one. Revoking takes effect immediately.
- Only administrators and dedicated sender addresses can use the API. Deactivating a user stops their keys.
- We do not store your message bodies for API sends beyond a short retention period used for the sent log.
Not available yet
We would rather tell you than let you find out. These are planned and not part of the API today:
- Delivery, bounce and complaint webhooks
- Inbound email: receiving replies into your software
- An Idempotency-Key header and a message status lookup
- A versioned address (/v1) and a dedicated API hostname
- IP allow-lists for keys
Tell us which one you need first: it decides what we build next.
Changelog
- November 2026. The API now has its own host,
api.cyphex.tech, with the stable pathPOST /v1/emails. The old addresshttps://mail.cyphex.agency/api/emails/sendkeeps working; new integrations should use the new one. - November 2026. Every error now carries a stable
code.bccrecipients are delivered and counted toward limits. Per-key limits of 120 requests a minute and 5,000 emails a day. Invalid JSON answers400instead of500.
Support
Human support in your time zone: email support@cyphex.agency or message us on WhatsApp. Our SLA targets 99.5% availability. When you write, include the time of the request and the code you received, never your key.