Roles & Permissions
The platform recognizes exactly five platform roles, defined in apps/api/src/auth/roles.ts as PLATFORM_ROLES:
export const PLATFORM_ROLES = [ "member", "company-admin", "company-member", "teros-ops", "teros-ops-admin",] as const;Role assignment lives in WorkOS. The API verifies the JWT via WorkOSGuard, and RolesGuard reads the per-route @Roles(...) metadata (the key is auth:roles, set by the @Roles decorator in apps/api/src/auth/roles.decorator.ts).
Anything not gated by @Roles(...) must carry the @Public() marker from apps/api/src/common/decorators/public.decorator.ts, or live on a *Public*Controller class. The build-time generator that produces the Permissions Matrix refuses to ship if any controller method declares neither.
member
Section titled “member”A candidate on apps/pool. Operates exclusively on their own profile and applications. A member can create their candidate profile, edit it, upload a resume, browse public job postings, apply to job postings, withdraw their own application, and read the status of every application they submitted. They have no visibility into other candidates’ data, into companies’ internal hiring state, or into platform-level operations.
company-admin
Section titled “company-admin”The primary stakeholder for a company on apps/ops — typically a founder, CTO, or VP of Engineering. They can manage their company’s profile, invite and remove company-members, view every candidate that has interacted with their company’s job postings, manage their job postings end-to-end, advance candidates through the hiring pipeline, and view the audit log scoped to their organization. All access is scoped to their own organizationId — they cannot see other companies’ data.
company-member
Section titled “company-member”A non-admin company collaborator (e.g. a hiring manager or engineering lead). They can read most things the company-admin sees inside their own organization, but write access is restricted to pipeline participation: leaving feedback, updating interview status, and shortlisting candidates. They cannot manage billing, invite other members, edit the company profile, or create/close job postings.
teros-ops
Section titled “teros-ops”A Teros internal operator. Cross-company access for day-to-day platform operations: managing candidate profiles, prepping job postings on behalf of companies, advancing candidates, reviewing applications, and reading the audit log across all organizations. They are explicitly not allowed to apply to jobs themselves (that surface is member-only), and they cannot perform irreversible admin actions such as migrating legacy storage — those are teros-ops-admin-only.
teros-ops-admin
Section titled “teros-ops-admin”A privileged Teros operator. Everything teros-ops can do, plus the irreversible / destructive surface: legacy file migrations, sensitive cross-org admin tooling on the ops dashboard, and platform-wide configuration. In the matrix this role appears almost exclusively on routes that mutate platform-wide state or run one-shot maintenance jobs.
anonymous / public
Section titled “anonymous / public”Any route that does not require authentication. There are two flavors:
@Public()-marked methods — for exampleGET /healthfor liveness checks.*Public*Controllerclasses —PublicResumeController,PublicInvitationController,PublicJobPostingsController,PublicJobPostingRequestsController. These expose token-gated endpoints used in shareable links (e.g. a public resume URL with an opaque token). They are unauthenticated at the role layer but enforce authorization through the URL token itself.
The deployed docs site is also fully unauthenticated — Cloudflare Zero Trust gates the site at the edge, separately from the API.
See it for real
Section titled “See it for real”Jump to the Permissions Matrix for the live, code-derived table of every endpoint and the roles it requires.