Skip to content
Reference · keys, scopes and sessions

API Authentication

Machine-to-machine requests authenticate with an API key. Dashboard sessions authenticate with a short-lived JWT. Both are accepted on the same endpoints; a key is what you want for integrations and scripts.

01 — The short version

Sending your key

Preferred
X-API-Key: ddmarc_<64 hex characters>

Or as a bearer token, which is handy for clients that only expose an Authorization header:

Also accepted
Authorization: Bearer ddmarc_<64 hex characters>

If both headers are present, X-API-Key takes precedence.

02 — Setup

Getting your API key

Three steps, once
01 — Open API key settingsIn the dashboard, go to Settings → Organization → API Keys. Only organization admins can create or revoke keys.
02 — Create a key with the right scopeGive it a descriptive name (for example "Production sync" or "CI pipeline"), pick a scope, and optionally set an expiry in days. Scope defaults to read.
03 — Copy and store it securelyThe full key is returned once, at creation. Only a short prefix is stored for display, so it cannot be shown again. Put it in a secrets manager.
03 — Over the API

Key management endpoints

Keys can also be managed over the API. These routes require an admin session or an admin-scoped key.

GET/api/v1/api-keysList API keys
POST/api/v1/api-keysCreate an API key
DELETE/api/v1/api-keys/{key_id}Revoke an API key
POST /api/v1/api-keys
curl -X POST "https://api.ddmarc.com/api/v1/api-keys" \
  -H "X-API-Key: $DDMARC_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI pipeline",
    "scope": "read",
    "expires_in_days": 90
  }'
201 Created
{
  "id": 7,
  "name": "CI pipeline",
  "key": "ddmarc_4f1c...b93a",
  "key_prefix": "4f1c8ad2",
  "scope": "read",
  "expires_at": "2026-10-17T09:14:00Z",
  "created_at": "2026-07-19T09:14:00Z"
}

key is present only on this 201 response. Subsequent GET calls return key_prefix, scope, last_used_at and expires_at only, alongside the plan allowance fields limit, used and can_create.

04 — In practice

Making authenticated requests

cURL
curl -X GET "https://api.ddmarc.com/api/v1/domains" \
  -H "X-API-Key: $DDMARC_API_KEY"
JavaScript (fetch)
const response = await fetch(
  "https://api.ddmarc.com/api/v1/domains",
  { headers: { "X-API-Key": process.env.DDMARC_API_KEY } }
);
const domains = await response.json(); // bare array
Python (requests)
import os, requests

response = requests.get(
    "https://api.ddmarc.com/api/v1/domains",
    headers={"X-API-Key": os.environ["DDMARC_API_KEY"]},
)
domains = response.json()  # list[dict]
05 — What a key is

API key properties

Key format

The literal prefix ddmarc_ followed by 64 lowercase hex characters (32 random bytes) — 71 characters in total. Anything not starting with ddmarc_ is rejected outright.

Authority

A key belongs to the organization, not to the person who created it, and always resolves to the same organization context. What it may do is bounded by its scope — not by the creator's role.

Expiration

Keys do not expire by default. Pass expires_in_days at creation to set one. An expired key stops authenticating immediately.

Rotation

There is no in-place regenerate. Create a second key, deploy it, then revoke the old one — which means you need a spare slot under your plan's key cap.

A key, in full
ddmarc_4f1c8ad2e07b3915c6a48d2f0b7e9a31c5d84e60f27a1b93c4e5d6f7089a1b2c
06 — Authority

Scopes

Scope is chosen when the key is created and cannot be changed afterwards. Scopes are hierarchical: admin satisfies anything requiring write, which satisfies anything requiring read. A request needing more authority than the key carries fails with 403.

ScopeLevelGrants
readDefaultRead-only access to domains, reports, senders and analytics.
writeMutatingEverything in read, plus creating and modifying domains, senders, rules and uploads.
adminFullEverything in write, plus administrative routes such as team management.
Grant the narrowest scope that works. A read key that leaks cannot delete a domain.
07 — Allowances

Plan limits

Programmatic API access requires the Protect plan or higher. Requests are metered hourly and pooled across the whole organization, not per key.

MonitorNo API access—
Protect1,000 requests / hour1 key
Growth5,000 requests / hour3 keys
Professional10,000 requests / hour10 keys
Partner Starter25,000 requests / hourUnlimited
Partner25,000 requests / hourUnlimited
EnterpriseCustomUnlimited
Track usage on your side
Hitting the hourly cap returns 429 with Retry-After: 3600. No X-RateLimit-* headers are sent on successful responses, so there is nothing to budget against but your own count.
08 — Failures

Authentication errors

CodedetailCause
401Invalid API keyKey is unknown, malformed, expired or revoked. All four produce the same message, so a 401 never confirms that a key ever existed.
401Not authenticatedNo credential was sent at all.
403API key requires 'write' scope or higherThe route needs more authority than the key's scope.
403API access requires the Protect plan or higherReturned when creating a key on a plan without API access.
429API rate limit exceededHourly org-wide cap reached. Includes Retry-After: 3600.
Error response
{
  "detail": "Invalid API key"
}

Errors are always a flat object with a detail field. There is no nested error object and no machine-readable error code — branch on the HTTP status.

09 — The other credential

JWT sessions

The dashboard and the mobile view authenticate with JWTs, not API keys. You only need this if you are building something that signs a human in.

Access tokens are short-livedThey expire after 15 minutes and are sent as Authorization: Bearer, or as an httpOnly access_token cookie.
Refresh tokens last 7 days and rotateCall POST /api/v1/auth/refresh to exchange one. Each refresh token is single-use, so replaying an old one fails.
Several sign-in methods existMagic link, one-time code, passkeys (WebAuthn), and Google or Microsoft (work or school) sign-in, all under /api/v1/auth. The dashboard offers no password sign-in.
Scopes do not apply to JWTsA signed-in user's authority comes from their role. Scope enforcement is specific to API keys.
10 — Handling keys

Security practices

Never expose keys in client-side codeAPI keys should only ever be used server-side.
Use environment variablesNever hardcode a key in source you commit.
Ask for the smallest scopeMost integrations only ever read. Reserve write and admin for the jobs that mutate.
Set an expiry on throwaway keysA key issued for a migration or a one-off script should not outlive it.
Watch last-used timestampsEach key records last_used_at; a key that has gone quiet is a key you can revoke. Creation and revocation are written to the activity log.
11 — Day to day

Managing API keys

List

Every key with its name, prefix, scope, creation date and last-used timestamp, plus how many of your plan's key slots are in use.

Revoke

Revoking deletes the key record outright. It stops working immediately and cannot be restored — issue a new key instead.

Rotate

Create, deploy, then revoke. Keep the two overlapping long enough that nothing is mid-request when the old one goes away.