DOCUMENTATION · API v1
Pull your evidence into your own systems.
Everything the portal shows is available over HTTP: coverage state, open root causes, the append-only ledger and the generated documents. Read-only by default, JSON everywhere, and stable — v1 will not change shape under you.
Authentication
Keys are organization-scoped, hashed at rest, shown once, and revocable. Send yours as a bearer token on every request. Sign in, then request a key from support.
curl https://eaacompliance.org/api/v1/domains \
-H "Authorization: Bearer eaa_live_9f3c41a8b2e04d7f"
Conventions
Public ids are prefixed and stable: scn_ scans, jrn_ journeys, fg_ finding groups. Never parse meaning out of them.
All timestamps are UTC, ISO 8601, second precision. Ranges are inclusive of from, exclusive of to.
Keyset, not offset: pass before with the last id you saw. Stable while new entries arrive.
Breaking changes ship as /api/v2. Additive fields can appear in v1 at any time — ignore what you don’t know.
Errors & limits
Errors are JSON with a stable error code and a human message. The code is for your switch statement; the message is for your logs.
{
"ok": false,
"error": "invalid_key",
"message": "That API key is not valid or has been revoked."
}
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | A parameter is missing or malformed. |
| 401 | invalid_key | Missing, unknown or revoked key. |
| 403 | plan_required | The API is included from the Scale plan. |
| 404 | not_found | No such object in your organization. |
| 409 | capacity_reached | A scan was requested beyond this period’s capacity. |
| 429 | rate_limited | 60 requests/minute. Retry after the header says so. |
Domains
Every domain in your organization with its verification state, schedule and coverage figure.
{
"ok": true,
"domains": [
{
"id": 12,
"host": "beispiel-commerce.de",
"status": "verified",
"schedule": "daily",
"coverage": { "covered": 46, "total": 50, "failing": 4 },
"last_scan": "scn_k21mmd0q7x",
"verified_at": "2026-08-16T23:00:11Z"
}
]
}
Scans
Trigger a scan — the per-deploy hook. Counts against this period’s capacity like any other scan.
| Parameter | Type | Notes |
|---|---|---|
| domain_id | integer REQUIRED | A verified domain in your organization. |
| reason | string OPTIONAL | Free text stored on the ledger entry, e.g. a release tag. |
curl -X POST https://eaacompliance.org/api/v1/scans \
-H "Authorization: Bearer $EAA_KEY" \
-H "Content-Type: application/json" \
-d '{"domain_id": 12, "reason": "release v4.18.2"}'
Status and summary for one scan. Poll this, or subscribe to scan.completed and stop polling.
Findings
Root causes, not raw instances — ranked by severity and legal weight, the same order the portal shows.
| Parameter | Type | Notes |
|---|---|---|
| domain_id | integer OPTIONAL | Restrict to one domain. |
| status | string OPTIONAL | open · verifying · resolved |
{
"ok": true,
"summary": { "raw_instances": 371, "root_causes": 4 },
"findings": [
{
"id": "fg_8241",
"rule": "image-alt",
"title": "Images without a text alternative",
"criteria": ["1.1.1"],
"impact": "critical",
"legal_weight": "high",
"instances": 312,
"status": "open",
"sources": ["crawler", "journey"],
"reference": "https://eaacompliance.org/rules/image-alt"
}
]
}
Mark a root cause fixed from your pipeline. It moves to verifying — and only a scan can move it to resolved.
Coverage
All 50 criteria with state, method and evidence — the matrix as data. Ideal for a compliance dashboard of your own.
{
"covered": 46, "total": 50,
"criteria": [
{ "ref": "1.4.3", "level": "AA", "method": "automated",
"state": "fail", "root_causes": 1, "instances": 41 },
{ "ref": "3.2.3", "level": "AA", "method": "human",
"state": "verified", "by": "[email protected]",
"at": "2026-08-14T09:12:00Z" }
]
}
Ledger
The append-only record, newest first, keyset-paginated. Filter by domain_id and scope (verification, scan, coverage, system). Entries are never edited or deleted, so mirroring it into your own store is safe.
Exports
The accessibility statement as structured data — computed status, barriers with remediation state, method and enforcement contact — so you can render it inside your own site template and keep it current automatically.
Webhooks
Point a URL at your systems and stop polling. Deliveries retry with backoff for 24 hours; each carries an event id you can use to de-duplicate.
| Event | Fires when |
|---|---|
| scan.completed | A scan finishes — includes the summary and the new failing-criteria count. |
| finding.opened | A new root cause appears, or a resolved one reappears. |
| finding.resolved | A scan verifies zero remaining instances. |
| coverage.changed | A criterion changes state, including human sign-offs. |
| verification.warning | A domain’s TXT record went missing — the 72-hour grace window opened. |
| capacity.reached | The period’s capacity is spent and frequency has stepped down. |
Verifying signatures
Every delivery carries EAA-Signature: t=<unix>,v1=<hex> — an HMAC-SHA256 over "{t}.{raw body}" with your endpoint secret. Compare in constant time and reject anything older than five minutes.
$expected = hash_hmac('sha256', $t . '.' . $payload, $secret);
if (!hash_equals($expected, $v1) || abs(time() - $t) > 300) {
http_response_code(400); exit; // reject silently, log loudly
}
Recipe · block a deploy on a regression
Scan on release, wait for the event, fail the pipeline if a critical root cause appeared. Capacity-aware: a 409 means the period is spent, not that the site is clean.
# trigger, then let the webhook do the waiting
SCAN=$(curl -sf -X POST "$EAA/api/v1/scans" \
-H "Authorization: Bearer $EAA_KEY" \
-d "{\"domain_id\": 12, \"reason\": \"$GIT_TAG\"}" | jq -r .scan.id)
# or poll, if your pipeline prefers it
until [ "$(curl -sf -H "Authorization: Bearer $EAA_KEY" \
"$EAA/api/v1/scans/$SCAN" | jq -r .scan.status)" = "done" ]; do sleep 20; done
Recipe · publish your statement
Fetch the statement as data on a schedule and render it into your own footer template. It updates itself as remediation is verified — no one has to remember to edit a page after a fix lands.
Changelog
| Date | Change |
|---|---|
| 2026-08-17 | v1 published: domains, scans, findings, coverage, ledger, exports, webhooks. |
Additive fields are announced here and never break existing clients. Removals only ever happen in a new major version.