Documentation

Learn how to integrate and use DDMARC.

Docs/API Reference/Authentication
Security

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.

Quick Start

Send your key in the X-API-Key header:

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

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

Authorization: Bearer ddmarc_<64 hex characters>

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

Getting Your API Key

1

Open API key settings

In the dashboard, go to Settings → Organization → API Keys. Only organization admins can create or revoke keys.

2

Create a key with the right scope

Give 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.

3

Copy and store it securely

The 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.

Key Management Endpoints

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

MethodEndpointDescription
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
  }'
Response201 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.

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]

API Key Properties

Key format

A key is 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.

ddmarc_4f1c8ad2e07b3915c6a48d2f0b7e9a31c5d84e60f27a1b93c4e5d6f7089a1b2c

Authority

A key belongs to the organization, not to the person who created it, and it always resolves to the same organization context. What it may actually 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. Rotate by creating a second key, deploying it, then revoking the old one — which also means you need a spare slot under your plan's key cap.

Scopes

Scope is chosen when the key is created and cannot be changed afterwards. Scopes are hierarchical: admin satisfies anything that requires write, which satisfies anything that requires read. A request that needs 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.

Plan Limits

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

PlanRequest limitKeys
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

Hitting the hourly cap returns 429 with Retry-After: 3600. No X-RateLimit-* headers are sent on successful responses, so track usage on your side.

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 higher.Returned when creating a key on a plan without API access.
429API rate limit exceeded (...)Hourly org-wide cap reached. Includes Retry-After: 3600.
Error Response Example
{
  "detail": "Invalid API key"
}

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

JWT Sessions

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

  • Access tokens are short-lived. They expire after 15 minutes and are sent as Authorization: Bearer or as an httpOnly access_token cookie.
  • Refresh tokens last 7 days and rotate. Call POST /api/v1/auth/refresh to exchange one; each refresh token is single-use, so replaying an old one fails.
  • Several sign-in methods exist. Password, magic link, one-time code, passkeys (WebAuthn), and Google or Microsoft single sign-on, all under /api/v1/auth.
  • Scopes do not apply to JWTs.A signed-in user's authority comes from their role. Scope enforcement is specific to API keys.

Security Best Practices

  • Never expose keys in client-side code. API keys should only be used server-side.
  • Use environment variables. Never hardcode API keys in your source code.
  • Ask for the smallest scope. Most integrations only ever read. Reserve write and admin for the jobs that mutate.
  • Set an expiry on throwaway keys. A key issued for a migration or a one-off script should not outlive it.
  • Watch last-used timestamps. Each 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.

Managing API Keys

List

See 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 keys overlapping long enough that nothing is mid-request when the old one goes away.

Continue to API Reference