Authentication
Requests carry an API key as a bearer token:
Authorization: Bearer ds_live_a1b2c3d4e5f6... The API is a paid feature. Free accounts cannot create keys, and a key stops working if the
account moves to a plan without API access — the response is 403 naming the plan, not a 429 you could wait out. Each paid plan’s request rate is on the pricing page.
Create a key in the dashboard under API keys. The key is shown once, at creation. We store only a hash of it and a short prefix for lookup, so we cannot show it to you again or recover it — if it is lost, rotate the key.
Keys belong to an account
A key acts for the account it was created in, not for the person who created it. Requests made with it read and write that account’s data, and it keeps working after that person leaves the team. Revoke keys when someone leaves; changing their password does not affect them.
Scopes
A key carries one or more scopes and can do nothing outside them.
| Scope | Grants |
|---|---|
tests:write | Create tests — POST /v1/tests |
tests:read | Read your account’s tests — GET /v1/tests, GET /v1/tests/{token} |
domains:read | List your domains — GET /v1/domains |
dmarc:read | Read DMARC aggregate data for a domain — see below |
dmarc:read needs domains:read alongside it in practice. Every DMARC endpoint is addressed by
domain id, and GET /v1/domains is the only public route that returns one — a key holding dmarc:read alone has the permission but no way to name what it applies to. They are separate
scopes because reading aggregate data for a domain you already know is not the same permission as
enumerating every domain on the account.
Almost every integration needs both. tests:write creates a test; tests:read is the only
way to read the result of one, because a key-created test is not readable without a key (see
below). A key with tests:write alone can submit messages and never learn how they scored — useful
only if you rely on webhooks instead of polling.
A request whose key lacks the scope gets 403 and names what was missing:
{ "error": "API key missing required scope: tests:read" } Checking a key
GET /v1/whoami needs no scope and is the quickest way to confirm a key works and see what it
can do:
curl -s https://api.deliversight.com/v1/whoami
-H "Authorization: Bearer $DELIVERSIGHT_API_KEY" {
"account_id": "018f3c2a-7b41-7e3d-9c02-5a1f6d8e4b77",
"scopes": ["tests:write", "tests:read"]
} Rotation
Rotating a key issues a new secret and invalidates the old one immediately. There is no overlap period, so deploy the new value before rotating, or accept a gap.
Reading a result always needs your key
Read a test you created with GET /v1/tests/{token} and your key. That is the only way.
There is a second, keyless endpoint — GET /v1/shared/{token} — but it serves only the
anonymous tests created on the website, and returns 404 for anything created with a key,
however valid the token. That is deliberate rather than an oversight: an API-created address
transits your own sending path, so it can appear in a bounce, a log or an ESP dashboard. If the
token alone unlocked the report, every one of those places would leak your results. Your key is
the credential; the token is only an identifier.
The practical consequence: a key-created test is not shareable as a link. If you want a result someone can open without credentials, run the test from the website instead.
Keys are for servers
Never put a key in a browser, a mobile app, or anything else a user can read. The public API sends no CORS headers for key-authenticated requests, so browser calls will fail — that is intentional rather than an oversight to work around.