Organizations
The Organization is the top-level tenant boundary in Adjudon —
the unit billing attaches to, the unit data isolation runs against,
and the unit every per-tenant feature gates against. Members,
workspaces, API keys, and invites live in their own collections
(OrgMember, Workspace, ApiKey, OrgInvite); this resource
covers what's stored on the Organization document itself: the
name, the data-retention setting, the SSO configuration, the SCIM
token, the branding fields, and the danger-zone destructive
actions. Tested against API version v1. JWT auth on every
endpoint; settings mutations require admin or owner; danger-
zone actions require owner. Plan-gated features (sso,
orgLogo, scim) return 403 UPGRADE_REQUIRED when the org's
plan is below the gate.
What lives on Organization vs elsewhere
The Organization document is intentionally lean:
Lives on Organization | Lives elsewhere |
|---|---|
name (unique across all orgs) | Members → OrgMember collection |
dataRetentionDays (7-3650) | Workspaces → Workspace collection |
setupCompleted, onboarding | Workspace API keys → ApiKey collection |
logoUrl, primaryColor | Pending invites → OrgInvite collection |
ssoConfig (OIDC + SAML + SCIM hashes) | Per-agent keys → Agent.apiKey |
settings (industry, region, timezone, audit toggles) | Billing details → billing sub-document on the same doc |
| Stripe customer + subscription state | Trace history → DecisionTrace collection |
This page covers the endpoints that read or mutate fields stored on the Organization document. Member CRUD lives at Team API; billing lives at Billing API.
Endpoints
POST /api/v1/org
DELETE /api/v1/org
GET /api/v1/org/settings
PATCH /api/v1/org/settings
PATCH /api/v1/org/setup
POST /api/v1/org/logo
POST /api/v1/org/sso/test
GET /api/v1/org/sso/activity
POST /api/v1/org/scim/token
DELETE /api/v1/org/scim/token
DELETE /api/v1/org/traces
Create an organization
POST /api/v1/org
Any authenticated user can create a new organisation; the user
becomes the owner of the new org via a fresh OrgMember row.
Switching between orgs after creation goes through
POST /api/v1/auth/switch-org. Errors: 400 VALIDATION_ERROR
(missing or duplicate name), 401, 500.
Get / update settings
GET /api/v1/org/settings
PATCH /api/v1/org/settings
The settings block carries the day-to-day org configuration:
| Field | Default | Description |
|---|---|---|
name | (required at create) | Unique organisation name |
dataRetentionDays | 90 | 7-3650; Governance+ gates the customisation surface but the field is always readable |
settings.industry | 'technology' | Editorial only — segments dashboard defaults |
settings.region | 'eu' | EU is the only supported region today |
settings.timeZone | 'Europe/Berlin' | Used for digest scheduling |
settings.billingEmail | null | Falls back to owner email when null |
settings.ipWhitelistEnabled | false | Reserved (placeholder) |
settings.auditLoggingEnabled | true | Cannot be disabled to false; the field exists for forward-compatibility |
settings.gdprComplianceEnabled | true | Reserved (placeholder); GDPR posture is contractual, not toggleable |
settings.piiMaskingEnabled | false | Adds a second pass over already-scrubbed payloads on dashboard render |
primaryColor | #0F7B6C | Hex; controls accent in branded PDF exports |
A PATCH validates the editable subset and writes one
org.settings.updated entry to the
Operations Audit Log. Errors:
400 VALIDATION_ERROR, 401, 403 (role), 500.
Onboarding setup
PATCH /api/v1/org/setup
Marks onboarding milestones from the dashboard wizard. Body
accepts setupCompleted: true, onboarding.currentStep, and
onboarding.completedSteps[]. Owner-only. Used by the dashboard's
guided setup; SDK callers do not normally hit this endpoint.
Branding
POST /api/v1/org/logo
Plan-gated by orgLogo (Scale+). Body is a base-64 data URI
written directly to Organization.logoUrl; the dashboard limits
the upload to a 200 KB image client-side. The logo surfaces on
branded PDF exports and on the dashboard chrome. Errors: 401,
403 UPGRADE_REQUIRED, 500.
The ssoConfig sub-document
The ssoConfig block is the largest sub-document on the
Organization. The most-touched fields are:
| Field | Purpose |
|---|---|
enabled | Master toggle; false means SSO is configured but not enforced |
provider | 'oidc', 'saml', or 'none' |
domain[] | Email domains that route to this org's IdP (multi-domain support since WS-3 — acme.com + acme.de + acme.co.uk share one config) |
clientId, issuerUrl | OIDC entry-point fields |
clientSecret | OIDC client secret; select: false so it never leaks via API responses |
samlIdpEntityId, samlIdpSsoUrl, samlIdpX509Cert | SAML IdP-side configuration (public, not secret) |
samlSpEntityId, samlSpAcsUrl | SP-side metadata Adjudon advertises to the IdP |
callbackUrlOverride | Per-org HTTPS override for OIDC redirect_uri and SAML ACS — lets a customer survive a backend domain rename without re-registering at the IdP |
requireMfaEvenWithSso | When true, Adjudon enforces TOTP on top of SSO (paranoid mode for regulated industries) |
samlAuthnContext | Optional SAML AuthnContextClassRef requirement (PasswordProtectedTransport / MultiFactor / TimeSyncToken) |
samlNameIdFormat | emailAddress (default), persistent, unspecified, or transient — ADFS / on-prem AD typically need persistent |
roleMapping | JIT role-mapping rules with re:-prefixed regex pattern support |
The PATCH /settings surface accepts every editable field above
under a nested ssoConfig body. clientSecret is write-only;
read responses mask it as <set> or null.
SSO test connection
POST /api/v1/org/sso/test
Plan-gated by sso (Governance+). Validates an admin-entered
OIDC issuer or SAML metadata URL against the live IdP discovery
document before persisting the configuration. The test does not
touch Organization.ssoConfig; it returns either
{ ok: true, discoveredIssuer, supportedScopes } or
{ ok: false, reason } with a curated reason vocabulary
(unreachable, invalid_metadata, expired_certificate,
mismatched_issuer, redirect_uri_unsupported, …). Rate-limited
on its own bucket (ssoTestRateLimit); credential-stuffing the
discovery endpoint of a third-party IdP is throttled.
SSO activity log
GET /api/v1/org/sso/activity
Plan-gated by sso. Returns a cursor-paginated slice of the
Operations Audit Log filtered to
SSO-related actions: auth.login with metadata.method='sso',
scim.user.*, auth.logout for SAML SLO, plus the test-connection
events. Used by the dashboard's SSO tab to surface "who logged in
via Okta yesterday at 14:02." Cursor token is opaque; treat it as
a string and pass it back unchanged on the next request.
SCIM token rotation
POST /api/v1/org/scim/token — rotate (returns plaintext once)
DELETE /api/v1/org/scim/token — revoke (clears + disables SCIM)
Plan-gated by scim (Enterprise+). Generating a new token
invalidates the previous one with a 24-hour grace window:
the previous hash stays valid for 24 h so in-flight IdP requests
do not 401-storm during a rotation. Use of the previous-hash
emits a scim.token.legacy_used audit entry so admins can see
when their IdP is still on the old token.
DELETE is the explicit-revoke path: both hashes clear and SCIM
is disabled entirely. There is no grace for explicit revocation.
The plaintext is returned exactly once on rotate; thereafter only
the last 4 characters are surfaced via the dashboard. See the
SCIM 2.0 API for what the token authorises.
Danger zone: purge traces
DELETE /api/v1/org/traces
Owner-only. Hard-deletes every DecisionTrace for the org. The
SHA-256 Decision Hash Chain entries are preserved (the chain
nullifies payload but keeps the chain shell intact, per Cardinal
Rule on the Audit Log) so the chain remains verifiable even after
a customer-initiated purge.
This is irreversible. The dashboard prompts for the org name as
a re-entry confirmation; the API does not. Errors: 401,
403 (role), 500.
Danger zone: delete organization
DELETE /api/v1/org
Owner-only. Soft-deletes the organization, deactivates every
member, and revokes every API key. Stripe is not auto-cancelled
— the owner cancels the subscription via the Customer
Portal first; orphaned subscriptions on a deleted org return
410 Gone to subsequent webhook events.
Common gotchas
- Org name is globally unique.
Organization.namecarries a unique index across every customer's org. Two customers cannot both create "Acme GmbH"; the second hits400 VALIDATION_ERROR. settings.regionis read-only as'eu'today. The field exists for forward-compatibility; Frankfurt eu-central-1 is the only data-residency region. PATCHing it to anything else is silently rejected.auditLoggingEnabledcannot be set tofalse. The field is on the schema, but the audit logger does not consult it — audit logging is a Cardinal Rule, not a toggle. Documented to prevent operator confusion.- SCIM rotation has a 24-hour grace; explicit revoke does not. Rotate when planning a maintenance window; revoke when responding to a security event.
- Idempotency. The
Idempotency-Keymiddleware is wired only onPOST /traces; org mutations do not auto-receive replay protection. The uniquenameindex defends against duplicate creation.
See also
- Team API — member CRUD, invitations, role changes
- Billing API — the Stripe facade driving plan changes
- SCIM 2.0 — what the token from
/scim/tokenauthorises - Auth API — the OIDC + SAML
endpoints
ssoConfigconfigures - Plans & Features —
the
sso,orgLogo, andscimfeature gates - Error Codes — the full error taxonomy