Compliance Mapping
The Compliance Mapping API stores per-clause attestations for
the audit-frameworks Adjudon supports today: ISO/IEC 42001 (18
clauses), EU AI Act Annex IV, and NIST AI RMF. The static clause
catalogue (clause text, default status, auto-satisfaction hints)
ships with the dashboard; this resource is the per-org overlay
that records the deployer's actual status, evidence narrative,
and reviewer attribution. Tested against API version v1.
Mounted at /api/v1/compliance. JWT auth on every endpoint;
plan-gated by complianceMapping (Governance+).
What lives where
| Layer | What it stores | Where |
|---|---|---|
| Static catalogue | Clause text, default status, auto-satisfaction hints | frontend/src/data/iso42001-mapping.ts (and equivalent files for other frameworks) |
| Per-org attestation | Status override, evidence narrative, reviewer attribution | ComplianceMappingNote collection — this resource |
| Legacy free-text map | Plain string per clause; no status, no reviewer | Organization.complianceNotes (kept for migration; new writes go to ComplianceMappingNote) |
The frameworks enum is iso-42001, eu-ai-act, nist-rmf.
The statuses enum is covered, partial, gap,
not-applicable. Defaults come from the static catalogue; this
API records the deployer's overrides only.
The ComplianceMappingNote object
| Field | Type | Required | Description |
|---|---|---|---|
_id / id | string | yes | MongoDB ObjectId |
organizationId | string | yes | Owning org |
framework | enum | yes | iso-42001, eu-ai-act, nist-rmf |
clauseId | string | yes | Framework-specific identifier (e.g. A.6.2.4); max 32 chars |
status | enum | no | covered, partial, gap, not-applicable; default gap |
evidence | string | no | Free-text narrative; max 4,000 chars |
reviewedAt | string (ISO 8601) | no | Set on each PATCH |
reviewedBy | string | no | User ObjectId of the reviewer |
reviewedByEmail | string | no | Email snapshot at PATCH time |
createdAt / updatedAt | string (ISO 8601) | no | Standard timestamps |
The compound unique index (organizationId, framework, clauseId)
makes every attestation upsert-safe — the same clause cannot
have two competing entries.
Endpoints
GET /api/v1/compliance/mapping?framework=iso-42001
PATCH /api/v1/compliance/mapping
GET /api/v1/compliance/mapping-notes
PUT /api/v1/compliance/mapping-notes
The first two are the structured path (current) and the recommended integration. The second two are the legacy free-text path kept for any caller still using the prior shape; reads merge both sources.
Get the per-framework rollup
GET /api/v1/compliance/mapping?framework=iso-42001
Returns the deployer's full attestation set for the requested framework, plus a coverage rollup the dashboard renders directly.
curl "https://api.adjudon.com/api/v1/compliance/mapping?framework=iso-42001" \
-H "Authorization: Bearer $ADJUDON_JWT"
| Query parameter | Required | Description |
|---|---|---|
framework | no | One of iso-42001 (default), eu-ai-act, nist-rmf |
Response (200 OK):
{
"success": true,
"data": {
"framework": "iso-42001",
"totalClauses": 18,
"notes": [
{
"clauseId": "A.6.2.4",
"status": "covered",
"evidence": "FRIA wizard records intended use; Hash Chain anchors every decision.",
"reviewedAt": "2026-04-29T14:02:11.000Z",
"reviewedByEmail": "[email protected]"
},
{ "clauseId": "A.7.2", "status": "partial", "evidence": "Drift dashboards live; data-quality KPI not yet wired." }
]
}
}
totalClauses is the static-catalogue total for the requested
framework; the dashboard divides notes by total to render the
coverage percentage. Errors: 400 VALIDATION_ERROR (unknown
framework), 401, 403 UPGRADE_REQUIRED (plan), 500.
Upsert one clause attestation
PATCH /api/v1/compliance/mapping
| Body field | Required | Description |
|---|---|---|
framework | yes | One of iso-42001, eu-ai-act, nist-rmf |
clauseId | yes | Max 32 chars; framework-specific (e.g. A.7.2) |
status | no | One of covered, partial, gap, not-applicable |
evidence | no | Up to 4,000 chars |
curl -X PATCH https://api.adjudon.com/api/v1/compliance/mapping \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{
"framework": "iso-42001",
"clauseId": "A.7.2",
"status": "partial",
"evidence": "Anomalies dashboard live; full data-quality KPI scheduled Q4 2026."
}'
import csv, os, requests
JWT = os.environ["ADJUDON_JWT"]
url = "https://api.adjudon.com/api/v1/compliance/mapping"
with open("clause-attestations.csv") as f:
for row in csv.DictReader(f):
r = requests.patch(url,
headers={"Authorization": f"Bearer {JWT}"},
json={
"framework": row["framework"],
"clauseId": row["clauseId"],
"status": row["status"],
"evidence": row["evidence"],
},
)
r.raise_for_status()
The handler does five things atomically: upserts the
ComplianceMappingNote document, stamps reviewedAt,
reviewedBy, and reviewedByEmail from the caller's JWT,
writes a compliance.mapping.updated entry to the
Operations Audit Log, appends an entry
to the SHA-256 Hash Chain, and
returns the resulting note plus the new coverage rollup. An
auditor reading the chain six months later can reconstruct
exactly who attested what when.
Multi-framework orgs (an entity certifying ISO 42001 and
attesting against EU AI Act Annex IV simultaneously) PATCH each
framework independently — the unique index is per
(org, framework, clauseId), so the same clauseId string can
exist on different frameworks without collision.
Errors: 400 VALIDATION_ERROR (missing or out-of-enum field;
clauseId longer than 32 chars; evidence longer than 4,000 chars),
401, 403, 500.
Legacy free-text endpoints
GET /api/v1/compliance/mapping-notes
PUT /api/v1/compliance/mapping-notes
The legacy shape stored a single string per clause keyed by
clauseId, with no status, no reviewer, no evidence-vs-note
distinction:
{ "A.6.2.4": "Covered by FRIA + Hash Chain.", "A.7.2": "Partial." }
These endpoints are retained for any caller that still uses the
prior shape. Reads merge both sources (legacy strings overlay
the structured ComplianceMappingNote collection); writes
update only the legacy Organization.complianceNotes Mixed map.
New integrations should use the structured /mapping
endpoints; the legacy surface will be deprecated per the
Versioning policy with at least 90 days
notice.
How an auditor consumes this surface
A typical pre-audit gap analysis reads this surface in three
passes. First, GET /mapping?framework=iso-42001 to pull the
current attestation state and the coverage percentage. Second,
the auditor cross-references each partial and gap row
against the operator's planned remediation; the evidence
narrative explains where each gap sits and what the operator
intends to do. Third, the auditor pulls the matching
Audit Log slice filtered to
compliance.mapping.updated entries to confirm the attestation
history has not been retroactively rewritten — the
Hash Chain anchors prove it.
Common gotchas
- Static catalogue vs per-org overlay. The clause text and default status are not editable via this API — they ship with the dashboard's data file. This API only records the deployer's overrides. Adding a new framework or new clauses ships in a Adjudon release.
- Evidence has a 4,000-character ceiling. Long evidence narratives should link out to a separate document (Confluence, Notion, GRC tool); the field is for the audit- readable summary, not the full procedure.
- Reviewer attribution survives user deletion. The
reviewedByEmailsnapshot is captured at PATCH time so an auditor reading the attestation a year later can attribute the change without joining against the liveUsercollection. - Default status is the floor, not the ceiling. A
covereddefault in the static catalogue can be downgraded topartialorgapvia PATCH; the audit log records the change and the reviewer email. The ISO 42001 page explains the framing. - Idempotency. The
Idempotency-Keymiddleware is wired only onPOST /traces; mapping mutations do not auto-receive replay protection. The compound unique index on(organizationId, framework, clauseId)defends against duplicate notes; PATCH is itself idempotent (re-PATCH with the same body produces the same end state).
See also
- ISO 42001 Compliance — the framework page that consumes this API surface
- Audit Log API — where every
compliance.mapping.updatedevent lands - Hash Chain — the tamper-evident anchor for clause attestations
- Plans & Features —
the
complianceMappingandiso42001Pdffeature gates - Error Codes — the broader error taxonomy