API Keys

API keys let you access your DMARC Examiner data programmatically through the REST API. Use them to integrate DMARC monitoring into your own tools, dashboards, or automation workflows.

Creating an API Key

  1. Go to Settings > API Keys
  2. Click Create API Key
  3. Enter a descriptive name for the key (e.g., "Monitoring Dashboard", "CI Pipeline")
  4. Copy the key immediately — it will only be shown once

Each organization can have up to 5 active API keys. Keys are scoped to the organization and provide read access to all domains, reports, and alerts within it.

API Key Format

API keys follow the format dex_ followed by 32 hexadecimal characters:

dex_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6

The dex_ prefix identifies the key as a DMARC Examiner API key. In the Settings UI, only the first 8 characters are displayed for identification.

Authentication

Include your API key in the X-API-Key header with every request:

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/domains

Available Endpoints

Dominios

Listar dominios

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/domains

Respuesta:

{
  "data": [
    {
      "id": "uuid",
      "name": "example.com",
      "status": "active",
      "verified_at": "2025-01-15T10:30:00Z",
      "last_report_at": "2025-03-20T08:00:00Z",
      "created_at": "2025-01-10T09:00:00Z"
    }
  ]
}

Crear un dominio

curl -X POST \
  -H "X-API-Key: dex_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name":"example.com"}' \
  https://api.dmarc-examiner.com/api/v1/domains

Devuelve 201 Created con el dominio creado. La respuesta incluye reporting_email y dmarc_record — cópialos en tu DNS para empezar a recibir reportes DMARC. Devuelve 409 Conflict si el dominio ya existe en la organización, o un error de límite de plan si superas el cupo.

Obtener un dominio

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/domains/{domain_id}

Verificar un dominio

Re-consulta el DNS para localizar tu reporting email y actualiza el estado del dominio si lo encuentra.

curl -X POST \
  -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/domains/{domain_id}/verify

Devuelve 200 OK con {"verified": true, "domain": {...}} en caso de éxito, o 422 Unprocessable Entity con la razón si el DNS aún no contiene el reporting email.

Eliminar un dominio

curl -X DELETE \
  -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/domains/{domain_id}

Devuelve 204 No Content en caso de éxito.

Comprobar registro DMARC existente (cualquier dominio)

Utilidad libre — no requiere que el dominio pertenezca a tu organización. Útil para due diligence antes de añadir un dominio nuevo.

curl -X POST \
  -H "X-API-Key: dex_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"domain":"example.com"}' \
  https://api.dmarc-examiner.com/api/v1/check-dmarc

Devuelve el registro DMARC existente (si lo hay), la política parseada y las direcciones de reporting.

Reports

List reports

curl -H "X-API-Key: dex_your_key_here" \
  "https://api.dmarc-examiner.com/api/v1/reports?domain_id=uuid&page=1&limit=20"

Query parameters:

Parameter Type Description
domain_id string Filter by domain UUID
date_from integer Unix timestamp, filter reports starting from
date_to integer Unix timestamp, filter reports ending at
page integer Page number (default: 1)
limit integer Results per page (default: 20, max: 100)

Get a single report

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/reports/{report_id}

Returns the report with all individual records, including geolocation data.

Get report statistics

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/reports/{report_id}/statistics

Returns aggregated statistics grouped by country, ASN, and authentication result.

Export report as CSV

curl -H "X-API-Key: dex_your_key_here" \
  -o report.csv \
  https://api.dmarc-examiner.com/api/v1/reports/{report_id}/export/csv

Alerts

List alerts

curl -H "X-API-Key: dex_your_key_here" \
  "https://api.dmarc-examiner.com/api/v1/alerts?severity=critical&status=pending"

Query parameters:

Parameter Type Description
severity string Filter by severity: info, warning, critical
status string Filter by status: pending, sent, dismissed
page integer Page number (default: 1)
limit integer Results per page (default: 20, max: 100)

Dismiss an alert

curl -X PUT \
  -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/alerts/{alert_id}/dismiss

Reportes forenses (plan Pro o superior)

Los reportes forenses (DMARC RUF) contienen el detalle del mensaje fallido — IP del remitente, MAIL FROM original, resultados de autenticación por comprobación. Estos endpoints devuelven 403 Forbidden si el plan actual no incluye reportes forenses.

Listar reportes forenses

curl -H "X-API-Key: dex_your_key_here" \
  "https://api.dmarc-examiner.com/api/v1/forensic-reports?domain_id=uuid&page=1&limit=20"

Parámetros: domain_id, date_from, date_to, source_ip, auth_failure (dmarc / spf / dkim), page, limit.

Obtener un reporte forense

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/forensic-reports/{forensic_report_id}

Devuelve el registro completo incluyendo resultados de autenticación parseados, geolocalización de la IP origen y las cabeceras originales (cuando estén disponibles).

Resumen (últimos 7 días)

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/forensic-reports/summary

Estadísticas agregadas

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/forensic-reports/statistics

Webhooks (plan Pro o superior)

Los webhooks permiten suscribirse a eventos y que los enviemos por POST a tu URL. Hoy el sistema emite un único evento:

  • alert.created — se dispara cada vez que se crea una alerta en tu organización (procesamiento de reportes o detección de cambios DNS).

Otros tipos (report.received, domain.verified) NO se emiten actualmente.

Estos endpoints devuelven 403 Forbidden si el plan no incluye alertas avanzadas. Las entregas fallidas se reintentan dos veces (30 s, 120 s) y el webhook se auto-deshabilita tras 10 fallos consecutivos.

Listar webhooks

curl -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/webhooks

Cada webhook se crea con un secret que guardas en tu lado. Cada entrega va firmada con HMAC-SHA256 — el secret nunca viaja en la red.

Cómo se firma cada entrega

Cada POST a tu URL incluye:

Header Valor
Content-Type application/json
User-Agent DMARC-Examiner-Webhooks/1.0
X-Webhook-Timestamp Segundos Unix cuando firmamos el request
X-Webhook-Signature sha256=<hex> — ver abajo

Donde:

signed_value = "{X-Webhook-Timestamp}.{raw_request_body}"
signature    = hex(HMAC-SHA256(secret, signed_value))
header       = "sha256=" + signature

Cómo verificar

  1. Rechaza requests donde |now - X-Webhook-Timestamp| > 5 minutos.
  2. Recalcula la firma esperada con tu secret guardado y el body crudo.
  3. Compara con igualdad en tiempo constante (nunca ==).
  4. Solo entonces parsea el body.

Node.js

import crypto from 'node:crypto'

function verify(req, secret) {
  const timestamp = req.headers['x-webhook-timestamp']
  const sigHeader = req.headers['x-webhook-signature']
  const rawBody = req.rawBody // string de bytes, NO JSON parseado

  if (!timestamp || !sigHeader?.startsWith('sha256=')) return false
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex')

  const got = sigHeader.slice('sha256='.length)
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(got, 'hex'))
}

Python

import hmac, hashlib, time

def verify(headers, raw_body: bytes, secret: str) -> bool:
    ts = headers.get('X-Webhook-Timestamp')
    sig = headers.get('X-Webhook-Signature', '')
    if not ts or not sig.startswith('sha256='):
        return False
    if abs(time.time() - int(ts)) > 300:
        return False
    signed = f"{ts}.{raw_body.decode()}".encode()
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig[len('sha256='):])

PHP

function verify(array $headers, string $rawBody, string $secret): bool {
    $ts = $headers['X-Webhook-Timestamp'] ?? null;
    $sig = $headers['X-Webhook-Signature'] ?? '';
    if (!$ts || !str_starts_with($sig, 'sha256=')) return false;
    if (abs(time() - (int) $ts) > 300) return false;
    $expected = hash_hmac('sha256', $ts . '.' . $rawBody, $secret);
    return hash_equals($expected, substr($sig, strlen('sha256=')));
}

Errores comunes

  • Los frameworks parsean el JSON antes de tu handler, pero la firma es sobre los bytes crudos. Captura el body crudo antes del parseo (Express: bodyParser.raw(), Symfony: $request->getContent(), Flask: request.get_data()).
  • Usar == en lugar de comparación de tiempo constante es una vulnerabilidad timing-attack.
  • Saltarse la ventana de timestamp es una vulnerabilidad replay-attack.

Crear un webhook

curl -X POST \
  -H "X-API-Key: dex_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/dmarc-events","name":"Producción"}' \
  https://api.dmarc-examiner.com/api/v1/webhooks

La URL debe ser HTTPS. Cada organización tiene un máximo de webhooks activos.

Actualizar un webhook

curl -X PUT \
  -H "X-API-Key: dex_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"is_active":false}' \
  https://api.dmarc-examiner.com/api/v1/webhooks/{webhook_id}

Acepta cualquier combinación de url, name, is_active.

Eliminar un webhook

curl -X DELETE \
  -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/webhooks/{webhook_id}

Enviar evento de prueba

curl -X POST \
  -H "X-API-Key: dex_your_key_here" \
  https://api.dmarc-examiner.com/api/v1/webhooks/{webhook_id}/test

Devuelve el resultado de la entrega (HTTP status, tiempo de respuesta, error si lo hay).

Rate Limits

API requests are limited to 100 requests per minute per API key. When the limit is exceeded, the API returns a 429 Too Many Requests response.

Revoking a Key

To revoke an API key:

  1. Go to Settings > API Keys
  2. Click the delete icon next to the key you want to revoke
  3. Confirm the revocation

Revoked keys stop working immediately and cannot be restored. You will need to create a new key and update your integrations.

Security Best Practices

  • Never expose API keys in client-side code or public repositories
  • Use environment variables to store keys in your applications
  • Create separate keys for different integrations so you can revoke them independently
  • Rotate keys periodically by creating a new key, updating your integration, and then revoking the old one