emailsecops
Docs

REST API and webhooks

Domains, checks, DKIM, DMARC, alerts and webhooks over JSON, with a team token.

Bearer authentication with a team token (create one under MCP & API in the dashboard). JSON in, JSON out; results are wrapped in data. Rate limits and monthly quotas follow your plan and are returned in X-Quota-Limit / X-Quota-Used and X-RateLimit-*. Read-only tokens can call every GET plus the compute-only POSTs (verify, generate, bulk email checks).

Interactive API reference · machine-readable OpenAPI 3.1 spec (endpoints and webhook payloads). The REST API and the team MCP server are on the Starter, Pro, Enterprise plans.

Base URL

https://emailsecops.com/api/v1

DKIM

GET /dkim/lookup?domain=example.com[&selectors=google,s1]

Live lookup of any domain: given selectors only, or discovery across the default selectors of every major provider. Per selector: key type, bits, SHA-256 fingerprint, CNAME delegation and owning provider, flags, every RFC 6376/8301/8463 defect and a corrected record when the fix is mechanical.

POST /dkim/verify {"message": "<raw RFC 5322 source>"}

Re-verifies every DKIM-Signature from scratch: canonicalization, body hash, RSA or Ed25519 signature against the published key, expiry, and alignment with the From domain. Up to 1 MB.

POST /dkim/generate {"domain", "selector", "algorithm": "rsa-2048|rsa-4096|rsa-1024|ed25519"}

A key pair and the exact TXT record (plus the zone-file form split into 255-byte strings). Not stored or logged; responses carry Cache-Control: no-store.

GET /domains/{id}/dkim?days=30

Selector inventory of a monitored domain: status and health, provider, key age and bucket (under 6 months, over 6 months, over 2 years), rotations, which selectors and signing domains sign mail per DMARC reports, and findings.

POST /domains/{id}/dkim/selectors {"selector"}

Monitor a selector we cannot discover (Amazon SES tokens, dated Postmark keys). Looked up immediately.

GET /domains/{id}/dkim/selectors/{selector}

One selector with its key history: every fingerprint seen, when, and why it was retired (rotated, revoked, removed).

DELETE /domains/{id}/dkim/selectors/{selector}

Stop monitoring a selector you added (409 while it is published).

Monitoring

GET /domains · POST /domains {"name"} · DELETE /domains/{id}
GET /domains/{id}

Latest posture, recommendations, fix-it records, verification record and the DMARC report address.

POST /domains/{id}/verify · POST /domains/{id}/check
GET /domains/{id}/checks · /domains/{id}/diffs

Score history and every detected change with severity, including DKIM key rotations.

GET /domains/{id}/dmarc?days=30

Alignment rate, sources, failing known senders, enforcement advice and the suggested record.

GET /alerts[?open=1] · POST /alerts/{id}/resolve

Alerts, newest first; resolving one sends alert.resolved to your webhooks.

Intelligence (stateless)

GET /intel/domain?domain=example.com[&full=1]

Live posture: score, grade, records, blacklists, recommendations. full=1 returns the complete analysis.

GET /intel/[email protected]

Single-address risk: syntax, MX, provider, free/disposable/role, domain policy strength, score and reasons.

POST /intel/emails {"emails": [...]}

Up to 100 addresses per call.

Example

curl -H "Authorization: Bearer eso_live_…" \
  "https://emailsecops.com/api/v1/dkim/lookup?domain=example.com"

Webhooks

Endpoints subscribe to event types (or *) and optionally to some domains. Manage them in the dashboard or with GET|POST /webhooks, PATCH|DELETE /webhooks/{id}, POST /webhooks/{id}/test, POST /webhooks/{id}/rotate-secret, GET /webhooks/{id}/deliveries and POST …/deliveries/{id}/redeliver. Endpoints per plan: Starter 1, Pro 5, Enterprise 25.

domain.createdA domain was added to the team.
domain.verifiedDomain ownership was verified; monitoring started.
domain.deletedA domain was removed.
check.completedA posture check finished (every run, with score and grade).
posture.changedA check found differences from the previous one (all changes in one event).
dkim.selector.addedA new DKIM selector was published.
dkim.selector.removedA DKIM selector no longer resolves.
dkim.key.rotatedThe public key behind a DKIM selector changed.
dkim.key.revokedA DKIM key was revoked (empty p=).
dkim.record.changedA DKIM record changed without a new key (flags, tags).
dkim.key.staleA DKIM key has been published longer than the rotation threshold (daily audit).
dns.record.changedAn SPF, DMARC, BIMI, MTA-STS, TLS-RPT or MX record changed.
blacklist.listedA domain or sending IP was listed on a DNS blacklist.
blacklist.delistedA listing was cleared.
alert.createdAn alert was raised (drift, blacklist, DMARC failure spike, expiry, DKIM finding).
alert.resolvedAn alert was marked resolved.
dmarc.report.receivedA DMARC aggregate report was ingested.

Each delivery is a JSON POST: {"id": "evt_…", "type": "dkim.key.rotated", "api_version": "2026-10-10", "timestamp": "…", "data": {"domain": {"id", "name"}, …}}. Signatures follow Standard Webhooks, so any of its libraries (or Svix's) verifies them:

webhook-id: evt_01k…          # stable across retries: dedupe on it
webhook-timestamp: 1760090400 # reject if older than 5 minutes
webhook-signature: v1,<base64(HMAC-SHA256(key, "{id}.{timestamp}.{raw body}"))>
X-EmailSecOps-Event: dkim.key.rotated
X-EmailSecOps-Delivery: 8812-1   # delivery id and attempt, for support
// key = base64-decode(secret without "whsec_")
$expected = base64_encode(hash_hmac('sha256', "$id.$timestamp.$body", $key, true));
foreach (explode(' ', $signatureHeader) as $sig) {   // two signatures during a secret rotation
    if (hash_equals("v1,$expected", $sig)) { /* authentic */ }
}

Answer 2xx within 10 seconds. 5xx, 408, 425, 429 and timeouts are retried 7 times over about two days; other 4xx are final. After 20 failed deliveries in a row an endpoint is disabled and the team owner is emailed. Delivery logs are kept 30 days.

MCP server (AI agents)

The same team, plan and quota also serve AI agents at https://emailsecops.com/mcp: Claude, ChatGPT, Cursor and VS Code sign in with OAuth, other clients send a team token. Clients, tools and permissions: AI agents (MCP).

DMARC report ingestion

Each domain has an address <token>{{ config('emailsecops.rua.domain') }}. Point your inbound mail hook at POST /api/webhooks/dmarc with the raw report (or JSON {recipient, filename, content_base64}) and X-Signature: sha256=HMAC(body, secret). Reports can also be uploaded from the domain page.