InboxGuard Reports API
DMARC aggregate reports and scan history.
DMARC aggregate reports and scan history.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/inboxguard-io:inboxguard-io-reports-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: InboxGuard Reports API
version: 1.2.0
summary: Email deliverability monitoring, DMARC reporting, and DNS authentication scanning.
description: 'The InboxGuard REST API lets developers and AI agents run on-demand deliverability scans (SPF, DKIM, DMARC, MTA-STS, TLS-RPT, BIMI, and DNS blocklists), re-verify raw email headers, manage monitored domains, read DMARC aggregate reports, list alerts, and manage API keys.
## Authentication
Authenticated endpoints accept an InboxGuard API key as a bearer token:
```
Authorization: Bearer ig_live_xxxxxxxxxxxxxxxxxxxxxxxx
```
Mint keys in the dashboard (Settings → API keys) or via `POST /api-keys`. Keys carry scopes: `read` (GET only) or `full` (all methods). See https://inboxguard.io/auth.md.
## Errors
Every error returns the same structured envelope (`#/components/schemas/Error`): `{ "error": { "code": "RATE_LIMIT", "message": "...", "requestId": "..." } }`.
## Rate limits
The anonymous `POST /scan-domain` endpoint allows 5 requests/hour per IP; authenticated callers get their plan''s hourly limit. `POST /scan-domain` responses carry `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (and `X-RateLimit-*` equivalents); 429 responses add `Retry-After`.
## Idempotency
`POST /scan-domain` accepts an optional `Idempotency-Key` header with real replay semantics: a retry with the same key from the same caller within 1 hour returns the stored response instead of re-running the scan, marked with an `Idempotency-Replayed: true` response header.
## Versioning
The API uses unversioned, stable URLs (one surface — no `/v1` path prefix). The current API version is echoed on every response in the `API-Version` header. Additive, backward-compatible changes (new endpoints, new optional fields) ship continuously without a version bump. Breaking changes — when they are ever needed — ship under a NEW version that you opt into by sending an optional `Accept-Version` request header; the existing behaviour remains the default so unversioned callers are never broken. Omit `Accept-Version` to always get the current stable version. Upcoming breaking changes are announced in these docs and at https://inboxguard.io/llms-full.txt in advance.
## Pagination
Only `GET /scans` is paginated: pass `?before=` (from the previous response''s `nextCursor`) to walk older scans. `GET /domains`, `GET /alerts`, and `GET /api-keys` return the full (limit-capped) list in one response — there are no `cursor` parameters.'
termsOfService: https://inboxguard.io/terms
contact:
name: InboxGuard Support
email: support@inboxguard.io
url: https://inboxguard.io/contact
license:
name: Proprietary
url: https://inboxguard.io/terms
x-deprecation-policy: 'Unversioned, stable URLs. When an endpoint or field is ever deprecated, responses carry a ''Deprecation: true'' header and a ''Sunset: <HTTP-date>'' header marking the removal date — at least 6 months out — plus a ''Link: <url>; rel="deprecation"'' pointer to migration notes. Opt into a future breaking version with the optional ''Accept-Version'' request header. Nothing is deprecated today.'
servers:
- url: https://api.inboxguard.io
description: Production
security:
- ApiKeyBearer: []
tags:
- name: Reports
description: DMARC aggregate reports and scan history.
paths:
/domains/{id}/report:
parameters:
- $ref: '#/components/parameters/DomainId'
get:
operationId: getDeliverabilityReport
tags:
- Reports
summary: Structured deliverability report for a domain
description: 'Returns a structured, on-demand deliverability report for one monitored domain: the latest score and letter grade, each check''s status (pass/warn/fail), the top issues to fix, the blocklist hit count, the effective DMARC policy, and a `pdfUrl` pointing at the branded one-page PDF (`GET /domains/{id}/report.pdf`, same bearer token). Requires the domain to be tracked and scanned for the caller''s org (404 otherwise).'
parameters:
- $ref: '#/components/parameters/AcceptVersion'
responses:
'200':
description: Deliverability report
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/DeliverabilityReport'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/domains/{id}/report.pdf:
parameters:
- $ref: '#/components/parameters/DomainId'
get:
operationId: getDeliverabilityReportPdf
tags:
- Reports
summary: Branded one-page deliverability report (PDF)
description: 'Returns a branded one-page PDF of the domain''s latest deliverability report, generated in-process. The response is `application/pdf` with a `Content-Disposition: attachment` header (filename `inboxguard--.pdf`); over the JSON API it is base64-encoded binary. Same auth as `GET /domains/{id}/report` — send your bearer token. Requires the domain to be tracked and scanned for the caller''s org (404 otherwise).'
parameters:
- $ref: '#/components/parameters/AcceptVersion'
responses:
'200':
description: PDF report
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
Content-Disposition:
schema:
type: string
description: attachment; filename="inboxguard-<domain>-<date>.pdf"
content:
application/pdf:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/scans:
get:
operationId: listScans
tags:
- Reports
summary: List recent scans
description: 'Returns recent scans across all of the organization''s domains (thin rows: id, domain, run time, score), most recent first. Filter to one domain with `?domainId=`. Cursor pagination: pass the previous response''s `nextCursor` as `?before=` to walk older scans.'
parameters:
- name: domainId
in: query
required: false
description: Restrict to one monitored domain by its UUID (from `GET /domains`).
schema:
type: string
format: uuid
- name: before
in: query
required: false
description: ISO 8601 timestamp cursor — return scans strictly older than this. Use the previous response's `nextCursor`.
schema:
type: string
format: date-time
- name: limit
in: query
required: false
description: Max scans to return (default 20, max 100).
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- $ref: '#/components/parameters/AcceptVersion'
responses:
'200':
description: Scan history
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/ScanList'
example:
items:
- id: 0b8c7f3a-1f6e-4c2a-9a3d-2f9d2f3e4a5b
domain_id: 8f14e45f-ceea-467f-aa65-4c4e28f6f3f1
domain: example.com
run_at: '2026-06-09T07:00:00Z'
score: 88
nextCursor: '2026-06-09T07:00:00Z'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/domains/{id}/dmarc-reports:
parameters:
- $ref: '#/components/parameters/DomainId'
get:
operationId: getDmarcReports
tags:
- Reports
summary: DMARC aggregate report data for a domain
description: 'Returns DMARC RUA aggregate report data for one monitored domain over the last N days: the org''s hosted `rua-…@reports.inboxguard.io` inbox (with a ready-to-publish DMARC snippet), per-day per-reporter rollups, top sending sources, and window totals. The domain id is the UUID from `GET /domains` — resolve a domain NAME to its id by listing domains and matching `domain`. Requires a plan with DMARC ingest (403 otherwise).'
parameters:
- name: days
in: query
required: false
description: Lookback window in days (default 30; values above 90 are clamped to 90).
schema:
type: integer
minimum: 1
maximum: 90
default: 30
- $ref: '#/components/parameters/AcceptVersion'
responses:
'200':
description: DMARC aggregate summary
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/DmarcReportsResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
components:
headers:
ApiVersion:
description: The API version that served this response. The URL surface is unversioned and stable; this header reports the current version so clients can detect breaking-change rollouts. Send the optional `Accept-Version` request header to pin a version once a future breaking version is published.
schema:
type: string
responses:
InternalServerError:
description: Unexpected server error. Any unhandled failure is normalised by the root middleware into the structured error envelope with code `INTERNAL_ERROR` and a `requestId` for support.
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: INTERNAL_ERROR
message: Internal server error
requestId: 9f8c1b2a-3d4e-4f5a-8b6c-7d8e9f0a1b2c
NotFound:
description: Resource not found. Returns the structured error envelope with code `NOT_FOUND`.
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Invalid request. Returns the structured error envelope with code `BAD_REQUEST`.
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Authenticated but missing required scope or role. Returns the structured error envelope with code `FORBIDDEN`.
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Missing or invalid credentials. Returns the structured error envelope with code `UNAUTHORIZED`.
headers:
API-Version:
$ref: '#/components/headers/ApiVersion'
WWW-Authenticate:
schema:
type: string
description: Bearer resource_metadata="https://api.inboxguard.io/.well-known/oauth-protected-resource"
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
parameters:
DomainId:
name: id
in: path
required: true
description: Monitored domain id (UUID, from GET /domains).
schema:
type: string
format: uuid
AcceptVersion:
name: Accept-Version
in: header
required: false
description: Optional API version opt-in. Omit to receive the current stable version (recommended). Send a specific version string only to opt into a future breaking-change version once one is published; unknown values fall back to the current stable version. The version actually served is echoed in the `API-Version` response header.
schema:
type: string
schemas:
DmarcReportsResponse:
type: object
properties:
domain:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
required:
- id
- name
days:
type: integer
description: Effective lookback window applied.
ruaInbox:
type: object
description: The org's hosted RUA mailbox — publish this in the domain's DMARC record.
properties:
address:
type: string
description: e.g. rua-abc123…@reports.inboxguard.io
configured:
type: boolean
description: True once at least one aggregate report has arrived for the org.
dmarcSnippet:
type: string
description: Ready-to-publish DMARC TXT value containing the rua address.
required:
- address
- configured
- dmarcSnippet
daily:
type: array
description: Per-day, per-reporting-source rollups.
items:
type: object
properties:
date:
type: string
description: YYYY-MM-DD
source:
type: string
description: Reporting receiver (e.g. google.com).
spfAligned:
type: integer
dkimAligned:
type: integer
passCount:
type: integer
failCount:
type: integer
topSenders:
type: array
description: 'Per-(source IP, From-domain) totals — "what''s sending mail as you" (top 25 by volume). Each row carries the forensic drill-down: spfAlignedCount/dkimAlignedCount show which mechanism aligned, and quarantineCount/rejectCount show DMARC enforcement.'
items:
type: object
properties:
sourceIp:
type: string
headerFrom:
type: string
count:
type: integer
passCount:
type: integer
failCount:
type: integer
spfAlignedCount:
type: integer
dkimAlignedCount:
type: integer
quarantineCount:
type: integer
rejectCount:
type: integer
sender:
type: string
nullable: true
description: Identified sending service or null.
senderCategory:
type: string
nullable: true
totals:
type: object
properties:
volume:
type: integer
pass:
type: integer
fail:
type: integer
sources:
type: integer
description: Distinct reporting sources in the window.
authSummary:
type: object
description: Org-wide SPF vs DKIM alignment + disposition rollup for the window — the forensic signal showing which mechanism is failing.
properties:
volume:
type: integer
spfAlignRate:
type: number
nullable: true
description: Percent of messages SPF-aligned.
dkimAlignRate:
type: number
nullable: true
passRate:
type: number
nullable: true
quarantined:
type: integer
rejected:
type: integer
required:
- domain
- days
- ruaInbox
- daily
- topSenders
- totals
- authSummary
DeliverabilityReport:
type: object
description: Structured deliverability report for a tracked domain (the JSON form of GET /domains/{id}/report). Summarizes the latest scan into a per-check pass/warn/fail status, the top issues, blocklist hit count, and DMARC policy, plus a link to the branded PDF.
properties:
domain:
type: string
description: The reported domain.
score:
type:
- integer
- 'null'
minimum: 0
maximum: 100
description: Latest overall 0–100 deliverability score (null if not yet scored).
grade:
type:
- string
- 'null'
enum:
- A
- B
- C
- D
- F
- null
description: Letter grade for the latest score.
scannedAt:
type:
- string
- 'null'
format: date-time
description: When the underlying scan ran (null if none).
generatedAt:
type: string
format: date-time
description: When this report was generated.
checks:
type: array
description: Per-check rollup. 'not_applicable'/'unverified' are scoring model v3 (2026-08-05) — 'not_applicable' means the check doesn't apply to this domain (excluded from the score, never rendered as a failure); 'unverified' means it couldn't be checked/determined this scan (render as neutral, never pass/fail). Both are absent (never emitted) for a report generated from a pre-v3 scan.
items:
type: object
properties:
key:
type: string
description: Check key, e.g. spf, dmarc, dkim, mtaSts, tlsRpt, mxTls, bimi, blocklist.
label:
type: string
status:
type: string
enum:
- pass
- warn
- fail
- unknown
- not_applicable
- unverified
issues:
type: array
description: Top issues to fix, worst-first. An issue for an 'unverified' check is always severity=info and reads as a verify action, never a confident failure.
items:
$ref: '#/components/schemas/Issue'
blocklistHits:
type: integer
description: Number of DNS-blocklist listings detected.
dmarcPolicy:
type:
- string
- 'null'
description: Effective DMARC policy (none/quarantine/reject), or null if no DMARC record.
scoreSubtitle:
type:
- string
- 'null'
description: e.g. "80/100 · scored on 65 of 83 applicable points · 1 check unverified" — explains the score's denominator when a check was not_applicable/undetermined this scan. Null when there's nothing to explain (pre-v3 scan, or nothing excluded).
pdfUrl:
type: string
format: uri
description: Auth-gated URL for the branded one-page PDF (GET /domains/{id}/report.pdf — send your bearer token).
required:
- domain
- score
- grade
- generatedAt
- checks
- issues
- blocklistHits
- dmarcPolicy
- pdfUrl
ScanList:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/ScanListItem'
nextCursor:
type:
- string
- 'null'
description: run_at of the last item when the page is full (pass as ?before=); null when there are no more.
required:
- items
- nextCursor
ScanListItem:
type: object
description: Thin scan-history row (snake_case fields). For the full per-check JSON of the latest scan, use GET /domains/{id}.
properties:
id:
type: string
format: uuid
domain_id:
type: string
format: uuid
domain:
type: string
run_at:
type: string
format: date-time
score:
type: integer
minimum: 0
maximum: 100
required:
- id
- domain_id
- domain
- run_at
- score
Error:
type: object
description: Structured error envelope returned by every non-2xx response.
properties:
error:
type: object
properties:
code:
type: string
description: Stable machine-readable code.
enum:
- BAD_REQUEST
- UNAUTHORIZED
- FORBIDDEN
- NOT_FOUND
- CONFLICT
- RATE_LIMIT
- UPSTREAM_ERROR
- INTERNAL_ERROR
- SERVICE_UNAVAILABLE
- DNS_TEMPORARY_FAILURE
message:
type: string
description: Human-readable description.
requestId:
type: string
description: Correlation id for support.
details:
description: Optional structured validation detail (e.g. zod field errors).
retryAfterSeconds:
type: integer
description: Present on RATE_LIMIT responses.
required:
- code
- message
required:
- error
Issue:
type: object
description: One finding inside a check.
properties:
severity:
type: string
enum:
- info
- warn
- critical
message:
type: string
required:
- severity
- message
securitySchemes:
ApiKeyBearer:
type: http
scheme: bearer
bearerFormat: InboxGuard API key (ig_live_… / ig_test_…)
description: 'PRIMARY auth: send an InboxGuard API key as a bearer token (`Authorization: Bearer ig_live_…`). Scopes: `read` (GET) and `full` (all methods). Mint a key at https://inboxguard.io/settings or per https://inboxguard.io/auth.md.'
OAuth2:
type: oauth2
description: 'OAuth-shaped wrapper around the same API key. The ONLY grant is client_credentials at POST https://api.inboxguard.io/oauth/token, where the client_secret IS an InboxGuard API key (HTTP Basic or body); the returned access_token is that same key. There is NO authorization_code flow, NO PKCE, and NO /oauth/authorize endpoint. Discovery: /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource on api.inboxguard.io. See https://inboxguard.io/auth.md.'
flows:
clientCredentials:
tokenUrl: https://api.inboxguard.io/oauth/token
scopes:
read: Read-only access (GET).
full: Full read/write access (all methods).
externalDocs:
description: API quickstart and guides
url: https://inboxguard.io/guides/api-quickstart