API reference / Send a message
POST /v1/send
Send a message
Queues one message for delivery. The domain in from must be verified on your account;
an unverified domain is rejected rather than sent unsigned.
Pass an Idempotency-Key header on anything you might retry. A repeated request with
the same key returns the original response instead of sending twice, and carries
Idempotent-Replay: true so you can tell the two apart. Keys are remembered for 24
hours and are scoped to your API key, so order-4471 means your order, not
somebody else's. Reusing a key with a different body is a 400 — that is
almost always a bug, and replaying the first message would hide it.
Headers
- Authorization string required
Bearerfollowed by your API key.- Content-Type string required
application/json.- Idempotency-Key string optional
- Up to 255 characters. Your own identifier for this send — an order id, a job id.
Body parameters
- from string required
- Sender address on a verified domain. Accepts
addr@domainorName <addr@domain>. The domain must match the one your key is scoped to. - to string | string[] required
- One recipient or an array. Each accepts a bare address,
Name <addr>, or{ name, email }. Each recipient counts once against your monthly volume. - subject string required
- Subject line.
- html string optional
- HTML body. Supply
html,text, or both — at least one. - text string optional
- Plain-text body. Sent as the alternative part when
htmlis also present. - cc string | string[] optional
- Copied recipients, same formats as
to. - bcc string | string[] optional
- Blind-copied recipients, same formats as
to. - reply_to string | string[] optional
- Where replies should go, if not the
fromaddress. - tags string[] optional
- Labels carried into the event log and every webhook for this message.
- headers object optional
- Custom headers. Anything Pharos sets itself — authentication, routing, the standard message headers — is rejected with
400rather than ignored, so you find out at the call rather than from a recipient.
Errors
error.field names it. Retry-After says when to resume.
Every error has the same shape: type is the stable machine-readable part,
message is for a human reading logs and may be reworded, field appears
when one field is at fault, and docs points at the page that explains it.
Response
- idstring
- Identifier for this message. Use it in the event log and match it against webhooks.
- statusstring
queued. A 202 means accepted for delivery, not delivered — delivery outcomes arrive by webhook and in the event log.- dkimstring
signed. Messages leave signed; an unverified domain cannot send at all.- dmarcstring
aligned. The signing domain matches thefromdomain the recipient sees.
curl -X POST https://api.pharos.email/v1/send \
-H "Authorization: Bearer $PHAROS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-4471" \
-d '{
"from": "receipts@yourapp.com",
"to": "customer@example.com",
"subject": "Your receipt from Acme",
"html": "<p>Thanks for your order.</p>",
"tags": ["receipt"]
}' {
"id": "msg_2f8Kq1Zx",
"status": "queued",
"dkim": "signed",
"dmarc": "aligned"
} {
"error": {
"type": "domain_unverified",
"message": "yourapp.com is not verified",
"field": "from",
"docs": "/docs/domains"
}
}