> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alforse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions and access

> How Alforse decides who can see and change what, from the module-action model to employee types, groups, invites, and shop-floor PIN accounts.

Alforse authorizes each user through explicit grants scoped to a company. Each grant combines a **module**, such as Sales or Inventory, with an **action**: view, create, update, or delete. For example, `sales_view` permits reading sales records, and `inventory_update` permits editing inventory. Grants do not imply other grants; a user with none can sign in but cannot access any module.

An **employee type** provides a grant template, with per-employee exceptions. See [People](/docs/reference/people.md) for employee records, [API keys](/docs/building/api-keys.md) for programmatic access under the same model, and [two-factor authentication](/docs/reference/two-factor.md) for sign-in security.

## The module-action model

A single permission is written `<module>_<action>` — `sales_view`, `inventory_update`, `purchasing_create`. The **modules** are a fixed set that the database accepts (the permission tables reject any other key):

Accounting, Documents, Invoicing, Inventory, Messaging, Parts, People, Printing, Production, Purchasing, Quality, Resources, Sales, Settings, Users, and Workflows. Two internal modules, Items and Timecards, are hidden from every permission screen (`HIDDEN_MODULES` in `apps/erp/app/hooks/usePermissionMatrix.ts`), so what you edit is the list above.

The **actions** are the same four (`ALL_ACTIONS` in `apps/erp/app/hooks/usePermissionMatrix.ts`); a module's row only shows the actions that module supports:

FieldType

<details>
  <summary>**view**</summary>

  Read records in the module. Without it, the module's pages are invisible to that user.
</details>

<details>
  <summary>**create**</summary>

  Add new records.
</details>

<details>
  <summary>**update**</summary>

  Edit existing records.
</details>

<details>
  <summary>**delete**</summary>

  Remove records.
</details>

Every permission screen in Alforse is the same widget: a matrix with a **row per module and a checkbox per action** (`apps/erp/app/components/PermissionMatrix.tsx`). Toggle a cell for one grant, a row to flip a whole module, or the header to select everything. The employee-type template, a single employee's overrides, the bulk editor, and even an API key's scopes all render this identical matrix, so the mental model is the same everywhere.

NOTE

**A grant is scoped to a company**

Internally each grant stores the list of companies it applies to, so one person can hold different permissions in each company they belong to. In the UI you always edit permissions for the company you're currently in. The special value `"0"` means "all companies" and is how a true cross-company administrator is represented. See [multi-company setup](/docs/reference/intercompany.md) for how a user spans several companies.

Alforse enforces these grants in two places at once. The app checks them to show or hide screens and buttons, and the database enforces the same grants through row-level security, so a request that skips the UI still can't reach data the grant doesn't allow. That second layer is what makes the same model safe for [API keys](/docs/building/api-keys.md), which carry scopes in exactly this `module_action` shape.

## Employee types

An **employee type** is a named permission template. Instead of hand-checking dozens of cells for every hire, you define a type once (say "Buyer" with full Purchasing and view-only Inventory) and assign it. Each type is edited with the same matrix under **Users → Employee Types** (available on plans that include custom permissions), and its grants become the starting point for anyone you assign to it.

HEADS UP

**Editing a type changes the template, not existing people**

Changing a type's permissions only sets the defaults for people assigned *after* the change. Employees already on that type keep the permissions they have. To push a change to existing staff, use the bulk permission editor below.

Two types are seeded and can't be deleted, marked by a stable `systemType` value so an admin can rename their display label without breaking Alforse's internal lookups:

FieldType

<details>
  <summary>**Admin**</summary>

  The full-access type. Assign it to the people who run the company's Alforse setup.
</details>

<details>
  <summary>**Console Operator**</summary>

  The type given to shop-floor PIN accounts. It's managed automatically when you add an operator (below), not something you assign by hand.
</details>

Every other type is yours to create, rename, and delete. A company can hold at most one type of each system kind.

## Per-person overrides and bulk edits

An employee's permission matrix under **Users → Accounts** starts with the grants from their employee type. Checking or clearing a cell creates a per-employee exception without changing the type. When you switch types, Alforse asks whether to replace the current grants with the new template or preserve them.

When a change needs to reach many people at once, use the **bulk permission editor**. Select the users, pick a mode, then set the matrix:

FieldType

<details>
  <summary>**Add Permissions**</summary>

  Layer the selected grants on top of whatever each user already has. Nothing is taken away.
</details>

<details>
  <summary>**Update Permissions**</summary>

  Replace each selected user's permission set wholesale with the matrix below.
</details>

This is the tool for propagating an employee-type change to existing staff, or for granting a new module to a whole department in one action. The two modes are the difference between "give everyone these too" and "make everyone exactly this" — reach for **Update** only when you mean to overwrite.

NOTE

**A permission change may take a moment to land**

Alforse caches each user's effective permissions for speed. When you change someone's grants, Alforse clears their cache so the new permissions apply on their next request. If a change ever seems not to have taken, it's almost always a stale session — signing out and back in forces a fresh read.

## Inviting people

You don't create passwords for people. You **invite** them by email, and they set up their own sign-in when they accept. Add an employee under **Users → Accounts** with their name, email, employee type, and location. Alforse creates their account, records an invite, and emails them a link to finish joining.

Because an invite is a real record, an employee has a clear **status** derived from it and their account:

<details>
  <summary>**Invited**</summary>

  The invite has been sent but not yet accepted. The person can't sign in until they follow the link.
</details>

<details open>
  <summary>**Active**</summary>

  The invite was accepted and the account is live. This is the normal working state.
</details>

<details>
  <summary>**Inactive**</summary>

  The account has been deactivated, or the invite was revoked before acceptance. No access.
</details>

An invite is a company-scoped record keyed by email, and it carries the permission set the person will start with, so their grants are ready the moment they accept. While it's outstanding you can **resend** the invite email or **revoke** it entirely, both from the accounts list. Deactivating an active employee removes them from the company, strips their grants, and marks them inactive; it doesn't delete the person, who may still belong to other companies. For the full picture of a person's record, see [people](/docs/reference/people.md).

## Shop-floor PIN accounts

**Console operators** sign in to [MES](/docs/reference/mes.md) on a shared station with a **4-digit PIN** instead of an email-based login. They are a separate account type.

Add one under **Users → Operators** (visible only when console mode is enabled for the company) with a name and a location. Alforse creates a lightweight account with no email login, assigns it the **Console Operator** employee type automatically, and generates a 4-digit PIN that is shown once, when the operator is created — Alforse stores only a hash of it, so copy it then. On the shop floor the operator taps their name and enters the PIN, which Alforse checks before starting their session. If a PIN is lost, reset it from the same screen to generate a new one; the old PIN stops working immediately. Resetting an operator's PIN takes `users_update`; setting a console PIN for any other employee (from the **Accounts** list) also takes `settings_update`.

NOTE

**An operator can graduate to a full user**

When a PIN account needs real ERP access, convert it: give it an email and a full employee type, and Alforse promotes the same person to a normal invited user without losing their history (`convertConsoleOperatorToUser` in `apps/erp/app/modules/users/users.server.ts`). Their old floor activity stays attached to the one identity.

PIN accounts are scoped to the floor by design. They carry only the grants their Console Operator type allows, which is why a lost or shared tablet can't be used to reach the office side of Alforse. For the operator experience itself — clocking on, reporting quantities, issuing material — see the [MES reference](/docs/reference/mes.md).

## Where each piece lives

**People**

The individual accounts these permissions apply to.

[Open](/docs/reference/people.md)

**API keys**

Programmatic access using the same module-action scopes.

[Open](/docs/building/api-keys.md)

**The MES app**

Where console operators sign in with a PIN and run the floor.

[Open](/docs/reference/mes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.