Authentication
Authenticate every request with a per-project API key.
The API authenticates with a per-project API key. Each key belongs to one project (one company) and can only act on that project's data.
The Bearer header
Send your key in the Authorization header on every request. There's no SDK yet —
call the API directly with fetch, requests, or curl.
curl https://sendandretain.com/api/v1/emails \
-H "Authorization: Bearer $AEM_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "[email protected]", "template": "welcome" }'A missing or invalid key returns 401 with code unauthorized.
Creating keys
Create keys in the dashboard under Settings → API keys. Keys are shown once at creation and stored only as a hash — copy the key immediately and keep it secret. Revoke a key at any time; revoked keys stop working instantly.
Keys are prefixed aem_.
Never expose an aem_ key in client-side code or commit it to source control.
Keys send real email on your behalf. Load them from an environment variable.
Authorization has two axes
A key's authority is two independent things, and conflating them is the most common mistake here.
The scope is ranked
A key satisfies any requirement at or below its own tier:
Prop
Type
There is no approve, send or full scope. send and full were rungs on
an older ladder; they are not rungs now, and the database rejects a key minted
with either.
The send grant is a separate boolean
Delivering mail to a real inbox — POST /api/v1/emails, and the template test
send — needs write and the send grant. The grant is not a tier above
write: a key can hold admin and still be refused a send.
Keeping them apart is what makes the two useful combinations expressible. In the dashboard you pick a role, not two axes:
| Role in the dialog | Scope | Send grant | Use it for |
|---|---|---|---|
| Send + manage | write | yes | Your application. What POST /api/v1/emails needs. |
| Manage only | write | no | A content or CI credential that authors templates but must never deliver. |
| Read only | read | no | Dashboards and reporting. |
| Admin | admin | yes | Provisioning domains and senders. |
Give each application the lowest combination that works. A key with the
send grant that leaks can send mail as you; an admin key that leaks can
repoint your sending domains.
When authorization fails
A key that authenticates but isn't allowed gets 403 forbidden — not 401.
401 means no usable credential at all: missing header, or an invalid, revoked
or expired key.
The 403 body names both axes, so you can branch without parsing the prose:
{
"error": {
"code": "forbidden",
"message": "This endpoint requires 'write' scope (manage templates, contacts and automations) — this key has 'read' scope.",
"required_scope": "write",
"key_scope": "read",
"request_id": "req_8fK2mQ"
}
}A refusal at the grant carries required_grant: "approve" instead of
required_scope. Branch on which field is present: one means "mint a higher
tier", the other means "this key may author but not deliver". Every operation in
the API reference states its required scope.
What API keys can never do
Two things are deliberately out of reach of every key, at every scope:
- Setting provider credentials. Your Resend or SendGrid API key only ever
enters through the dashboard.
GET /api/v1/connectionreturns the settings URL for a human to visit — there is no endpoint that accepts a provider secret, so a leakedaem_key can't redirect your mail through someone else's provider account. - Creating projects or minting keys. An
aem_key is bound to exactly one project, so there is no principal it could act as to create another one or issue new credentials. Both stay in the dashboard.