Deployer Profile
The Deployer Profile API captures the EU AI Act Article 26
deployer-compliance pack: the human-oversight assignment under
§ 2, the monitoring arrangements under § 5, the DPIA
cross-link under § 6, the inbound provider instructions-of-
use intake, and the cross-link to every FRIA the deployer has
authored. One profile per organisation; the deployer attests
each section and the audit log records every change. Tested
against API version v1. Mounted at
/api/v1/deployer-profile (with a dashboard alias at
/api/v1/deployer). JWT auth; plan-gated by
art26DeployerPack (Governance+).
Article 26 mapping
The schema mirrors Article 26 paragraph-by-paragraph — the auditor's expected reading order is the order the JSON document unfolds:
| Art. 26 paragraph | DeployerProfile field |
|---|---|
| § 2 Human oversight | oversight.{assignedTeam, trainedUserIds[], sopReference} |
| § 5 Monitoring of operation | monitoring.{sopReference, cadence, alertChannels[]} |
| § 6 Data Protection Impact Assessment | dpia.{completed, completedAt, documentRef} |
| Receipt of instructions-of-use from provider | instructionsOfUse[] |
| Cross-link to FRIA (Art. 27) | friaIds[] |
The profile is a singleton per org — unique index on
organizationId. Initial fetch lazily creates the document with
empty fields so the dashboard's first-load pattern works without
a bootstrap call.
The DeployerProfile object
| Field | Type | Description |
|---|---|---|
organizationId | string | Owning org (singleton) |
legalName | string | null | Legal entity name as it appears on filings |
contactEmail | string | null | Designated Article 26 contact |
oversight.assignedTeam | string | null | Free-text team or function name |
oversight.trainedUserIds[] | string[] | Adjudon User IDs of operators with documented Art. 14 training |
oversight.sopReference | string | null | URL or document reference to the oversight SOP |
monitoring.sopReference | string | null | Monitoring SOP reference |
monitoring.cadence | enum | continuous, daily, weekly, monthly, quarterly |
monitoring.alertChannels[] | string[] | Channels where monitoring alerts route (Slack, email, PagerDuty, etc.) |
dpia.completed | boolean | DPIA completion flag |
dpia.completedAt | string (ISO 8601) | Completion timestamp |
dpia.documentRef | string | null | Reference to the DPIA artefact (out-of-platform) |
instructionsOfUse[] | array | Each entry { providerName, systemName, documentRef, receivedAt, acknowledgedAt } |
friaIds[] | string[] | Cross-link to FRIA documents authored under Art. 27 |
completeness | number | Self-assessed 0-100 posture |
createdAt / updatedAt | string (ISO 8601) | Standard timestamps |
Endpoints
GET /api/v1/deployer-profile
PATCH /api/v1/deployer-profile
POST /api/v1/deployer-profile/instructions
POST /api/v1/deployer-profile/instructions/:id/acknowledge
POST /api/v1/deployer-profile/fria-link
POST /api/v1/deployer-profile/fria-links ── alias
The /fria-link and /fria-links paths share one handler;
the dashboard uses the plural alias, the legacy schema uses
the singular. The same is true of
/instructions/:id/acknowledge: :id accepts both a numeric
index (0, 1, …) and the dashboard's
ins-<index> form (ins-0).
Read or initialise the profile
GET /api/v1/deployer-profile
curl https://api.adjudon.com/api/v1/deployer-profile \
-H "Authorization: Bearer $ADJUDON_JWT"
A first-time call lazily creates the singleton with empty
fields (no error if the org has nothing yet) and returns the
freshly-initialised document. Errors: 401, 403 UPGRADE_REQUIRED (plan), 500.
Update the profile
PATCH /api/v1/deployer-profile
Partial-update against any nested field. The handler does
deep-merge on the three sub-documents (oversight,
monitoring, dpia) so a request that only updates
monitoring.cadence does not clear the rest of monitoring.
Re-computes completeness server-side after every PATCH and
returns the updated value.
curl -X PATCH https://api.adjudon.com/api/v1/deployer-profile \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{
"monitoring": {
"sopReference": "https://intranet.example.com/sops/ai-monitoring-v3",
"cadence": "daily",
"alertChannels": ["slack:#ai-ops", "pagerduty:ai-oncall"]
}
}'
Writes one compliance.deployer.updated audit entry per call,
with the changed-keys diff in metadata. Errors: 400 VALIDATION_ERROR (cadence outside enum), 401, 403, 500.
Ingest a provider instruction-of-use
POST /api/v1/deployer-profile/instructions
Records receipt of a provider's instruction-of-use document.
Body: { providerName, systemName, documentRef? }. The handler
stamps receivedAt = now and appends one entry to
instructionsOfUse[]. The deployer later posts an
acknowledgement once the operator has reviewed and integrated
the instruction.
Acknowledge an instruction
POST /api/v1/deployer-profile/instructions/:id/acknowledge
Stamps acknowledgedAt = now on the matching entry. The :id
parameter accepts both a numeric index and the dashboard's
ins-<index> form — the controller normalises before
lookup. Acknowledging an already-acknowledged instruction is a
no-op (idempotent at the resource level). Errors: 404 INSTRUCTION_NOT_FOUND, 401, 403, 500.
Cross-link a FRIA
POST /api/v1/deployer-profile/fria-link
POST /api/v1/deployer-profile/fria-links
Body: { friaId }. Adds the FRIA ObjectId to friaIds[],
de-duplicates against existing entries, and returns the updated
profile. Used to cross-reference an Article 27 FRIA from the
Article 26 deployer pack — the auditor reading either
artefact reaches the other in one hop.
Completeness scoring
The completeness field is recomputed server-side after every
PATCH and after every instructions / acknowledge /
fria-link mutation. The score is the proportion of the
following nine slots that are populated:
| Slot | Counted populated when |
|---|---|
legalName | non-empty string |
contactEmail | non-empty string |
oversight.assignedTeam | non-empty string |
oversight.trainedUserIds | array length ≥ 1 |
oversight.sopReference | non-empty string |
monitoring.sopReference | non-empty string |
monitoring.cadence | always counts (default continuous) |
dpia.completed | true |
friaIds | array length ≥ 1 |
Each slot contributes ~11.1%. A profile with all nine
populated reports completeness: 100; the dashboard's
Compliance Posture card reads this directly. The score is
designed to trend over time, not gate behaviour — an
80% profile is not auditor-ready by definition, and a 100%
profile still requires the auditor's own review of the linked
artefacts.
How an auditor consumes this surface
A typical Article 26 audit walks the profile in four steps:
GET /deployer-profileto pull the full state.- Cross-reference
oversight.trainedUserIdsagainst the operator's training-attestation records (off-platform). - Open each entry in
instructionsOfUse[]and confirmacknowledgedAtis set; an unacknowledged instruction is audit-evidence the deployer received provider documentation they have not integrated. - Walk every
FRIAID infriaIds[]via the FRIA API and verify the chain anchor on each approved attestation.
The Operations Audit Log records every
compliance.deployer.* mutation so the auditor can confirm the
profile state was not retroactively shaped right before the
audit window opened.
Common gotchas
- Plan-gated. The whole resource requires
art26DeployerPack(Governance+). Sandbox and Scale receive403 UPGRADE_REQUIREDon every endpoint. - One profile per org. The unique index on
organizationIdenforces it. APOSTto create a second profile is not exposed; the singleton is implicit. - Lazy initialisation. First
GETcreates an empty profile; do not pre-create. Frontends should treat a 200 with empty fields as the legitimate first-load state. - Two valid
:idshapes on acknowledge. Numeric index (0,1, …) and dashboardins-<index>form both work. Either shape resolves to the same entry. - Idempotency. The
Idempotency-Keymiddleware is wired only onPOST /traces. Acknowledging an already-acknowledged instruction is idempotent at the resource level; PATCH is itself idempotent (re-PATCH with the same body produces the same end state); FRIA-link de-duplicates server-side.
See also
- EU AI Act Compliance — the Article 26 obligations this resource records
- FRIA API — the Article 27
artefact cross-linked via
friaIds[] - Audit Log API — where every
compliance.deployer.*event lands - Plans & Features —
the
art26DeployerPackfeature gate - Error Codes — the broader error taxonomy