Onboarding
The Onboarding API is the dashboard's instrumentation surface for
the first-time user experience — the path from sign-up to
first trace, with the Data Processing Agreement (DPA) signing event
anchored into a tamper-evident chain along the way. The endpoints
do three things: record progress events, accept and verify the
DPA, and serve the legal documents the dashboard renders during
the wizard. Tested against API version v1. JWT auth on every
endpoint; mutations carry their own per-bucket rate limiters
because the surface is public-facing wizard flow that needs
spam-defence beyond the IP limiter.
The historic /api/onboarding workspace-wizard endpoints were
removed on 2026-05-02; per-agent onboarding now lives on the
Agents API under /api/v1/agents/onboarding/*.
This page covers what remains: org-level events, the DPA chain,
the demo-data seeder, and the legal-document service.
Endpoints
POST /api/v1/onboarding/events
GET /api/v1/onboarding/state
POST /api/v1/onboarding/dpa-accept
GET /api/v1/onboarding/dpa-verification
POST /api/v1/onboarding/seed-demo
GET /api/v1/onboarding/legal-docs
GET /api/v1/onboarding/legal-docs/:doc/:locale
Record an event
POST /api/v1/onboarding/events
| Body field | Required | Description |
|---|---|---|
type | yes | One of signup, email_verified, workspace_created, persona_selected, agent_created, trace_received, policy_created, export_downloaded, invite_sent, onboarding_completed |
persona | no | One of regulatory, reliability, internal_risk, security, other |
industry | no | Free-text, capped at 80 characters |
metadata | no | Object; only the whitelisted keys flow through (see below) |
The dpa_accepted event is not accepted on this endpoint
— it returns 400 INVALID_INPUT with a hint to use
POST /dpa-accept instead. Routing it through the events bus
would bypass the cryptographic chain anchor; the controller
rejects the bypass at the validator.
The metadata object is filtered against an allow-list to enforce
Cardinal Rule #4 (no trace payloads in onboarding events). Permitted
keys: agentId, policyId, traceId, inviteEmail, exportType,
latencyMs, invitedRole, sourceStep. String values cap at 500
characters; everything else is silently dropped.
curl -X POST https://api.adjudon.com/api/v1/onboarding/events \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{
"type": "agent_created",
"persona": "regulatory",
"industry": "Banking — DACH",
"metadata": { "agentId": "customer-support-bot", "sourceStep": "wizard_step_2" }
}'
Errors: 400 INVALID_INPUT (unknown type, unknown persona, or
dpa_accepted reroute), 401, 429 RATE_LIMIT_EXCEEDED
(onboardingEventsRateLimit), 500.
Read the org's onboarding state
GET /api/v1/onboarding/state
Returns the org-level summary: completedSteps[], currentStep,
persona, industry, the timestamps of the most recent event of
each type, and the setupCompleted boolean from
Organization.setupCompleted.
Used by the dashboard's wizard to resume mid-flight after a page
reload. Errors: 401, 500.
Accept the DPA
POST /api/v1/onboarding/dpa-accept
Records the org's acceptance of the Data Processing Agreement under GDPR Art. 28(3). The handler does three things atomically:
- Persists a
dpa_acceptedonboarding event with the document version + locale. - Writes a
compliance.dpa.acceptedentry to the Operations Audit Log with the accepting user's email + IP + the document's sha256 anchor. - Appends one entry to the SHA-256 Hash Chain so a regulator can later verify the DPA acceptance happened on the date the audit log claims.
The response includes the resulting chainHash so the
dashboard can render "DPA signed; chain anchored at sequence N"
without a follow-up call. Body: { documentVersion?, locale? };
both default to the latest published version + the user's
preferred locale.
This endpoint is not rate-limited at the per-event tier (events
are spammable; DPA acceptance is a one-time-per-version operation)
but does carry its own dedicated dpaAcceptRateLimit to defend
against abuse. Errors: 401, 429, 500.
Verify the DPA chain
GET /api/v1/onboarding/dpa-verification
Recomputes the chain entries belonging to this org's DPA history
and confirms each chainHash matches the chained
previousHash+payloadHash of the next entry. A 200 with
verified: true confirms the chain is intact end-to-end; a 200
with verified: false carries the index of the first broken link.
A regulator who wants to verify the DPA acceptance independently hits this endpoint with the org's JWT and reads the verification result without needing to reconstruct the chain themselves.
Seed demo data
POST /api/v1/onboarding/seed-demo
Triggers the demoDataSeeder to populate the caller's active
workspace with a synthetic agent, ten realistic decision traces
across the success / flagged / blocked / approved spectrum, two
example policies, and one auto-approval pattern in the observing
state. Used by the dashboard's wizard "show me an example" path
so a new operator can see the dashboards populated before their
own SDK is wired.
Synthetic traces carry metadata.source = 'onboarding-test' —
the trace pipeline filters them out of the
Agents.flipDraftIfFirstRealTrace check
so a user cannot fake their way through the wizard. They are real
trace records and count toward the org's monthly usage.
Legal documents service
GET /api/v1/onboarding/legal-docs
GET /api/v1/onboarding/legal-docs/:doc/:locale
The first endpoint returns the metadata catalogue (filename,
version, sha256 digest, canonical URL) for every legal document
the dashboard renders during onboarding: the DPA, the Terms of
Service, the Privacy Policy, the Imprint, the Subprocessors list,
the Cookie policy. Both de and en locales are published.
The second endpoint returns the full Markdown body of a single
document. :doc is one of dpa, tos, privacy, imprint,
subprocessors, cookies; :locale is one of de, en. The
sha256 digest in the metadata catalogue matches the body returned
by the body endpoint — the dashboard verifies this on render
and refuses to display a document whose digest has drifted.
The body endpoint carries its own legalDocBodyRateLimit because
the documents are larger payloads and a tight loop hitting them
is the spam pattern this limiter brakes.
Common gotchas
dpa_acceptedis dedicated. Trying to record it viaPOST /eventsis rejected with a hint to the dedicated endpoint. The cryptographic chain anchor lives on the dedicated path; the events bus does not write to the chain.- Allow-listed metadata. Arbitrary metadata keys are
silently dropped. If you need a new field surfaced, add it
to
ALLOWED_METADATA_KEYSin the controller and ship a new schema version per the Versioning policy. - Demo traces count toward usage.
seed-demowrites realDecisionTracerecords; the ten traces it generates count toward the org's monthly trace budget. Sandbox orgs have ample headroom; this is documented to prevent surprise. - Workspace-wizard endpoints removed.
/api/onboarding/*paths historically covered the workspace-creation wizard; those were removed on 2026-05-02. Per-agent onboarding lives at/api/v1/agents/onboarding/*now. - Idempotency. The
Idempotency-Keymiddleware is wired only onPOST /traces; onboarding mutations do not auto- receive replay protection. The DPA accept endpoint is deliberately re-runnable — a second acceptance under the same version writes a second chain entry, which is redundant but not harmful.
See also
- Audit Log API — where every
compliance.dpa.acceptedand onboarding event lands - Hash Chain — the tamper-evident anchor the DPA acceptance is written into
- Agents API — the per-agent onboarding flow that replaced the workspace wizard
- Organizations API — the
setupCompletedandonboardingfields this resource updates - Error Codes — the broader error taxonomy