Skip to main content

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 OrganizationLives elsewhere
name (unique across all orgs)Members → OrgMember collection
dataRetentionDays (7-3650)Workspaces → Workspace collection
setupCompleted, onboardingWorkspace API keys → ApiKey collection
logoUrl, primaryColorPending 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 stateTrace 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:

FieldDefaultDescription
name(required at create)Unique organisation name
dataRetentionDays907-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.billingEmailnullFalls back to owner email when null
settings.ipWhitelistEnabledfalseReserved (placeholder)
settings.auditLoggingEnabledtrueCannot be disabled to false; the field exists for forward-compatibility
settings.gdprComplianceEnabledtrueReserved (placeholder); GDPR posture is contractual, not toggleable
settings.piiMaskingEnabledfalseAdds a second pass over already-scrubbed payloads on dashboard render
primaryColor#0F7B6CHex; 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:

FieldPurpose
enabledMaster 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, issuerUrlOIDC entry-point fields
clientSecretOIDC client secret; select: false so it never leaks via API responses
samlIdpEntityId, samlIdpSsoUrl, samlIdpX509CertSAML IdP-side configuration (public, not secret)
samlSpEntityId, samlSpAcsUrlSP-side metadata Adjudon advertises to the IdP
callbackUrlOverridePer-org HTTPS override for OIDC redirect_uri and SAML ACS — lets a customer survive a backend domain rename without re-registering at the IdP
requireMfaEvenWithSsoWhen true, Adjudon enforces TOTP on top of SSO (paranoid mode for regulated industries)
samlAuthnContextOptional SAML AuthnContextClassRef requirement (PasswordProtectedTransport / MultiFactor / TimeSyncToken)
samlNameIdFormatemailAddress (default), persistent, unspecified, or transient — ADFS / on-prem AD typically need persistent
roleMappingJIT 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.name carries a unique index across every customer's org. Two customers cannot both create "Acme GmbH"; the second hits 400 VALIDATION_ERROR.
  • settings.region is 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.
  • auditLoggingEnabled cannot be set to false. 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-Key middleware is wired only on POST /traces; org mutations do not auto-receive replay protection. The unique name index 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/token authorises
  • Auth API — the OIDC + SAML endpoints ssoConfig configures
  • Plans & Features — the sso, orgLogo, and scim feature gates
  • Error Codes — the full error taxonomy