Skip to main content

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 fieldRequiredDescription
typeyesOne of signup, email_verified, workspace_created, persona_selected, agent_created, trace_received, policy_created, export_downloaded, invite_sent, onboarding_completed
personanoOne of regulatory, reliability, internal_risk, security, other
industrynoFree-text, capped at 80 characters
metadatanoObject; 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
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:

  1. Persists a dpa_accepted onboarding event with the document version + locale.
  2. Writes a compliance.dpa.accepted entry to the Operations Audit Log with the accepting user's email + IP + the document's sha256 anchor.
  3. 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.

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_accepted is dedicated. Trying to record it via POST /events is 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_KEYS in the controller and ship a new schema version per the Versioning policy.
  • Demo traces count toward usage. seed-demo writes real DecisionTrace records; 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-Key middleware is wired only on POST /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.accepted and 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 setupCompleted and onboarding fields this resource updates
  • Error Codes — the broader error taxonomy