Multi-tenancy
FixControl is multi-tenant. One tenant owns its own issues, agents, knowledge, memory, audit log, and inbox. A user belongs to exactly one tenant. Tenants cannot see each other's data. An admin in tenant A cannot fetch issues from tenant B by hand-crafting an API call — tenant isolation is enforced on every endpoint, with additional database-level guards in strict mode (see Security & audit).
The one exception is the separate platformAdmin flag. It is independent of the regular role and is never set by self-serve signup.
Creating a new tenant
Two paths:
- Self-serve signup at
/signup. Creates a new tenant, seeds the canonical AI team, creates the first user asadmin, signs them in. User lands on/app. - Companies page (platformAdmin only) — create a tenant on behalf of a customer.
Signup disambiguates company names — two independent signups for "Acme" become "Acme" and "Acme (2)", never silently merged.
Roles
Hierarchical (admin > manager > operator):
| Role | Can |
|---|---|
| Admin | User management, agents, codebases, settings, destructive admin actions (with two-person rule). |
| Manager | Assign issues, approve / reject / apply patches, mutate verification checklists, change retention. |
| Operator | Create workspaces, work on assigned items. No approval rights. |
Plus the platformAdmin flag. It grants cross-tenant read access (Companies view, audit aggregations) and is only granted by FixControl staff. Self-serve signup never sets it.
Project-scoped authorization
On top of the tenant roles above, access to project-bound data can be scoped per project. The model: a user acts inside a project only through an active project membership carrying a project role.
Project roles
Two system roles are seeded per tenant and cannot be edited:
- Project Admin — full management of one project: members, roles, settings, and the project-scoped resources.
- Project Member — read and participate: read project data, draft and request communications, execute workflows, use bound connectors.
You can also create custom roles, either tenant-wide (reusable across projects) or scoped to a single project. Permissions are picked from a fixed catalogue of capability strings (project, customer, communication, workflow, connector, audit, and policy permissions) — there are no free-form permissions.
Membership lifecycle
Every membership has a status (active, suspended, revoked) and an optional validity window (valid from / until) for temporary access. Anything that is not active and inside its window grants nothing — a revoked or expired membership bites immediately.
Deny by default, phased rollout
Enforcement sits behind the tenant-overridable flag PROJECT_AUTHORIZATION_ENABLED (off by default = compatibility mode: existing tenant-level access keeps working, and reliance on the legacy path is logged). With the flag on, project-bound data is deny by default: an operator without an active membership does not see the project — a guessed project id returns a plain 404.
Two things to know as an admin:
- Tenant admins and managers keep their tenant-wide reach. One central bypass rule lets them through; every bypass on an action path is audited. Operators never bypass — their access runs exclusively through an active membership. A handful of sensitive actions (for example approving external customer communication) demand a real membership even from an admin.
- Last-admin protection. A role change, suspension, or revoke that would leave a project with zero active members holding member-management rights is refused — a project can never be locked out of its own administration.
Managing members and roles
Member and role management lives on the project page (team access), not under tenant settings — and it is itself gated by project permissions: viewing requires project.members.read, changing requires project.members.manage (held by tenant admins/managers via the bypass, or by members with the Project Admin role). Managers cannot modify their own membership. Adding a member from another tenant is refused outright.
Sign-in methods
| Method | Surface | Used by |
|---|---|---|
| Email + password | /login | Tenants without an enterprise IdP. |
| OIDC SSO | /login → IdP redirect | Tenants with an enterprise IdP. |
You can run both in parallel during a migration. Configure under Settings → Identity.
OIDC SSO
Configure your IdP in the admin tenant settings:
- Go to Settings → Identity → OIDC.
- Add a provider with
issuer,clientId,clientSecret,redirectUri. - Set allowed email domains (the allowlist).
- Choose JIT provisioning if you want new users to be created on first sign-in, with a default role.
- Map claims:
email,name, optionallygroups.
What's enforced at the callback:
- Issuer match, audience match, nonce match,
expin the future, signed with the algorithm pinned by the JWK. - State + PKCE one-shot — replay attempts return 401.
- Domain allowlist check on the email claim.
If the email matches an existing user, the OIDC sign-in is linked to that user. If JIT is on and the domain is allowed, a new user is provisioned with the configured default role. Otherwise the sign-in is refused.
Today, OIDC links by email. SAML-style external-id-only linking is not supported.
SCIM 2.0 provisioning
If your IdP can push users (Okta, Azure AD, Google Workspace, others), connect via SCIM:
- Go to Settings → Identity → SCIM tokens (or Admin → SCIM tokens for platform admins).
- Mint a tenant-scoped bearer token. Shown once.
- In your IdP, configure the SCIM connector with:
- Endpoint: https://<your-deployment>/api/scim/v2 - Auth: Authorization: Bearer scim_…
- Select which Users and Groups to push.
Implemented endpoints:
UsersGET (paginated,userName eqfilter), POST, GET/PATCH/DELETE per id.GroupsGET (paginated), POST, GET/PATCH/DELETE per id.ServiceProviderConfig,Schemas,ResourceTypesdiscovery.
Group → role mapping is manual today. SCIM creates and updates users; you decide how groups map to FixControl roles in the UI. (Auto-mapping is on the roadmap.)
Deprovisioning
When your IdP pushes a user removal (DELETE /scim/v2/Users/{id} or active=false PATCH):
- The user is soft-deleted.
- All sessions are revoked.
- The deprovision event is audited.
Manual deactivation works the same — under Settings → Users, click Deactivate. Users can be re-activated as long as the soft-delete record remains.
Inviting users (without SCIM)
Tenant admin → Settings → Users → Create user. Name, email, initial password (min. 8 chars), role. The new user is auto-assigned to your tenant — no UI path can place someone in a different tenant.
The Last seen column shows the most recent successful sign-in from the audit log.

Admin list pages — DataTable
/settings/users, /settings/audit, the platform-admin Companies list, and the other admin tables all use the canonical operator DataTable. That means:
- Server-side sort + filter + pagination — large directories paginate cleanly; the URL captures the slice so you can paste a link to "all admins added in the last 7 days" and the colleague sees the same result.
- Keyboard map —
j/kto step rows,/to focus search,Enterto drill into a user/audit entry,[/]from a detail page step prev/next under the same filter. See Keyboard & command palette. - Bulk select —
xtoggles,Shift+xranges,Cmd/Ctrl+Aselects the visible page. Bulk Deactivate and bulk Reset password show in the sticky bulk toolbar; destructive actions confirm with the impacted list inline. - Saved views — for example
Active,Inactive,Admins, orOperators with no recent sign-in; a shared view means the next admin landing on the page picks up the same lens. - Audit context —
/settings/auditbenefits most: filter by actor + verb + time range, hitSave view, share the URL.
The admin tables share their keyboard map and command-palette commands with the user-facing operator pages — the same keys do the same things across the whole product.
Companies view
/companies is a platform-admin overview. Per tenant: issue count, agent count, member count, plan, contact mailbox, brand color. Create, edit, delete, or switch tenants. Only platform admins see all tenants — regular admins see only their own.
Security note — deleting a tenant from this view drops agents and cascades to all issues, knowledge, and memory. Irreversible. The action is gated by the two-person rule.
FAQ
Can a user belong to two tenants at once? No. A user lives in a single tenant. Use a second account with a different email if needed.
What happens to issues when I delete a user? Issues remain; only the user record is removed. Assignments to that user become loose references.
How do I make someone a platformAdmin? Not via self-service — contact FixControl support. Intentionally not exposed in the UI.
What if my IdP pushes a SCIM PATCH that adds a user from another tenant to my group? Refused server-side. Cross-tenant group membership is blocked at the SCIM layer.
Can I have both OIDC SSO and password sign-in active? Yes, during a migration. Disable passwords once SSO is fully rolled out.