ActiveManage Docs ← Back to activemanage.co.uk

Permissions

Permissions Overview

Permissions decide who can do what in ActiveManage. Every action — reading a datastore row, editing a field, deleting a record, accessing a page, calling an API endpoint — is permission-controlled. Get the permission model right and your application is secure by construction; get it wrong and you'll have data leaks, unauthorised changes, and very awkward incident reports.

The permission model in one sentence

Permissions are granted to security groups (not to individual users), and a user's effective permissions are the union of every group they belong to. There's no deny mechanism — permissions are grant-only.

Three implications of that single rule:

  • You can add a user to multiple groups and they get the union. Useful for layering roles.
  • You can't "deny" a permission by adding someone to a restricted group; you remove them from the granting group.
  • Audit is straightforward: "this user has access because they're in this group" is always the answer.

Three levels of granularity

The platform supports three increasingly fine-grained permission scopes:

Datastore-level

The broadest control. Decides whether a security group can read, write, edit, or delete rows on a whole table. Most permissions you configure will be at this level — "the Finance Team can read the orders table", "the Sales Team can write to the customers table".

Field-level

Narrows access within a datastore the user can otherwise see. Useful when different roles need different columns from the same table. For example: HR sees salary fields, line managers see role and start date, everyone sees name and email. Field permissions configure those exceptions.

Row-level

Restricts which specific rows a user can see or modify, even within a table they otherwise have access to. Typical use: "users can see only rows they created", "managers can see only rows from their department", "customers can see only their own orders".

Diagram showing the three permission levels in ActiveManage: datastore-level (whole table), field-level (specific columns), and row-level (specific rows)

Default-deny

Without an explicit grant, the answer is no. You don't need to "deny" things — anything you haven't explicitly allowed is implicitly forbidden. That's deliberate: it's much harder to accidentally over-share data when the default is locked-down.

Three example permission models

Example 1: Simple internal app

Two groups: Staff (can read/write the main tables), Admins (can also delete and manage users). Permissions are mostly at datastore level. Quick to set up, suitable for low-stakes applications.

Example 2: Multi-department business app

Per-department groups, each granted read/write only on tables relevant to their function. Field-level restrictions for sensitive columns (salary, performance ratings, customer card details). Row-level security on shared tables (each department sees their own customers).

Example 3: Customer-facing portal

Datastore-level grants for customers are limited (read-only on most things). Heavy use of row-level security to restrict customers to seeing their own data. Field-level on customer profiles to allow self-edit of some fields but not others.

Where you configure permissions

Permissions are configured per-datastore and per-resource:

  • Datastore-level: Architect Panel → Datastores → click the Permissions button on a datastore.
  • Field-level: Same screen, the Field Permissions tab.
  • Row-level: Toggle Row-Level Security on the datastore's main settings, then configure rules under Permissions.
  • Page / route permissions: On the Page Builder page or Friendly URL definition.
  • API permissions: On the API Client configuration.
Tip: Start with datastore-level only. Add field-level and row-level rules only where they're genuinely needed — over-engineering the permission model up-front leads to a tangled mess that's hard to debug. Add complexity as concrete requirements emerge.

Datastore-level Permissions

Datastore-level permissions are the broadest control: whether a security group can interact with a table at all. For 80% of permission configuration, this layer is where the work happens. Get datastore permissions right first; add field- and row-level rules later only where needed.

The four core actions

Every datastore-level permission grants one of four actions, with the option to mix and match per group:

  • Read — see rows in browse views, the API, and detail pages. Without Read, the datastore is invisible to the group.
  • Write (create) — add new rows. The user can fill in the New Record form and save.
  • Edit — change existing rows. The Edit button appears in row actions; the user can open the edit form and save changes.
  • Delete — soft-delete rows. The Delete button appears in row actions.

Granting transitively

The four actions don't auto-imply each other — granting Edit doesn't automatically grant Read — but in practice you almost always want them to align:

  • Write usually requires Read so users can see what they're creating against.
  • Edit always requires Read — there's no point letting someone change a row they can't see.
  • Delete usually requires Read and Edit for the same reason.

The platform doesn't enforce these alignments — you can have illogical combinations — but you should configure them deliberately rather than by accident.

Where to configure

From the Architect Panel → Datastores admin, find the datastore and click the Permissions button. The Permissions dialog opens with rows for each existing security group.

For each group, tick the actions the group should have. Save. Permissions take effect immediately on the next request from any user in that group.

Screenshot of the datastore Permissions dialog showing security groups as rows and Read/Write/Edit/Delete columns with checkboxes for each combination

Three real-world permission matrices

Example 1: Orders table on a B2B platform

Different groups need different access:

  • Sales Team: Read, Write, Edit. Can create new orders and update them.
  • Finance Team: Read, Edit. Can view all orders and update payment status, but can't create orders.
  • Customer Service: Read only. Can see orders but can't change them.
  • Customers (external): Read only, plus row-level security restricting them to their own orders.
  • Anonymous Visitors: No access at all.

Example 2: Internal knowledge base articles

  • All Users: Read. Anyone logged in can see the articles.
  • Knowledge Base Editors: Read, Write, Edit, Delete. Full control.
  • Anonymous Visitors: No access (internal-only).

Example 3: Customer profiles

  • All Users: No access — customer data is sensitive.
  • Customer Success Team: Read, Edit. View and update customer details.
  • Sales Team: Read only. Can see customer history when prospecting.
  • Finance Team: Read only.
  • Customers themselves: Read, Edit on their own row only (via row-level security).

Audit and review

Datastore permissions are one of the highest-leverage places to make a mistake — accidentally granting Write to a customer-facing group on the wrong table can leak data. Two practices to mitigate:

1. Review after every change

After granting or revoking permissions, click around as a representative user from each affected group. Verify they see what they should and don't see what they shouldn't.

2. Periodic audit

Quarterly, export the full permission matrix and review it with whoever owns the affected business function. People change roles, applications evolve — permissions need to keep up.

Note: The platform doesn't enforce that you read what you write — you can have a group that can create rows but can't see them afterwards. That's occasionally useful (anonymous form submissions are an example) but more often a misconfiguration. Spot-check the matrix for any group with Write but not Read.

Field-level Permissions

Datastore-level permissions decide whether a security group can see a table at all. Field-level permissions go one level deeper — they let you hide or restrict specific columns within a table the user can otherwise read or write.

This is one of the highest-leverage features in the platform for modelling real-world access policies. It means one datastore can serve multiple roles, each seeing only the fields appropriate to their job, without having to clone the data into role-specific tables.

Why field permissions matter

A real-world example. An HR team keeps a Staff datastore with rows for every employee. The columns include:

  • Name, email, phone (everyone needs).
  • Job title, department, manager (most teams need).
  • Salary, performance rating, disciplinary notes (only HR and direct manager need).
  • Bank details, tax number, national insurance number (only payroll need).

Without field permissions, you'd need three or four separate datastores or a complex set of views. With field permissions, one datastore serves them all — HR sees everything, line managers see most, sales reps see only the name/email subset, customers see nothing.

How to configure them

Open the datastore in the Architect Panel and click the Permissions button. The dialog has tabs for datastore-level permissions and for field permissions. The Field Permissions tab lists every field of the datastore down one axis and every security group across the other.

For each combination, set one of three states:

  • Read — the group can see the field's value.
  • Write — the group can set the field's value when creating or editing rows.
  • No access — the field doesn't appear at all for this group.

Defaults and inheritance

If a security group doesn't have an explicit field-permission row, it inherits the datastore-level permission for that field. So you only need to add field-permission rows where you want to differ from the datastore default.

Practical implication: don't configure field permissions for every field × every group. Configure them only for the exceptions. A typical datastore has 20 fields and 10 groups; that's 200 possible combinations, but you'll usually have under 20 actual field-permission rows because most fields inherit the default.

Where field permissions take effect

Permissions apply consistently across every place the data is rendered or accessed:

  • Auto-generated forms: fields without read access don't appear; fields with read-only access are shown but disabled.
  • Browse views: restricted fields are stripped from the column list before render.
  • Custom Query Views: the platform applies the same restrictions to query results.
  • The API: calls from a user without read access to a field get back the row with that field omitted.
  • Email templates: merge tokens for restricted fields render as empty strings.

Screenshot of the Field Permissions tab showing fields as rows and security groups as columns, with dropdown selectors per cell offering Read, Write, No Access options

Three patterns that come up often

Pattern 1: Sensitive column visible to one group only

Salary on a Staff table: HR has Read+Write, Direct Managers have Read, everyone else has No Access. Three explicit field-permission rows; everyone else inherits the datastore default (typically Read on the rest of the columns).

Pattern 2: Field editable only by certain roles

Status field on a Tickets table: Customer Service can Read+Write (they update the status). All Users can Read (they see the current status). Customers (external) can Read (on their own tickets, via row-level security).

Pattern 3: Field hidden from everyone except admins

Internal Notes field on a Customers table: Customer Success Team has Read+Write. Everyone else has No Access. Admins see it on the auto-generated edit form; customers, sales reps, and the API don't even know the field exists for that row.

Note: Field permissions don't affect SQL queries written in Custom Query Views or in raw PHP. If you're hand-coding access, you're responsible for applying the same restrictions in your query. The platform applies field permissions at the auto-generated form and API layer; bypassing those layers bypasses the permission too.

Row-level Security

Row-level security restricts which rows a user can see or modify within a datastore. Datastore-level permission says "can interact with this table"; row-level adds "and only these specific rows".

This is the platform's tool for multi-tenant data (one shared table, each tenant sees their own rows), owner-based access (users see only their own records), and department-segmented data (each team sees only their team's records).

Enabling row-level security

On the datastore's edit page, toggle Row-Level Security on. Then configure rules that decide which rows each group can access — typically based on a column on the row matching a property of the logged-in user.

Each rule has three parts:

  • Security group the rule applies to.
  • Match column on the row.
  • Match value — usually a context expression like {{currentUserID}} or {{currentTenantID}} that's evaluated per request.

If any of a user's groups has a matching rule, they can see the row. If no rule matches, they can't.

Three common rule patterns

Pattern 1: Owner-based

Rows where owner_id = current_user_id. Users can only see records they themselves created. Typical for personal data — a user's own bookings, their own messages, their own profile.

Pattern 2: Department-based

Rows where department = current_user_department. Useful when one shared table serves multiple business units that shouldn't see each other's data — e.g. customer records partitioned by sales region.

Pattern 3: Tenant-based

Rows where tenantid = current_tenant_id. This is handled automatically by the platform's multi-tenancy layer when multi-tenancy is on — every query is auto-filtered by the current tenant. Explicit rules are needed only for cross-tenant exceptions (admins who can see all tenants).

How rules combine

If a user belongs to multiple security groups, each with their own row-level rule, the resulting rules are OR-combined. So a user in both "Department A" and "Department B" groups would see rows where department = A OR department = B.

Combined with the union nature of group permissions, that's how you express hierarchical access. A manager who's in both the "Sales Team" group (rule: owner_id = current_user_id) and the "Sales Manager" group (rule: department = current_user_department) sees both their own records AND every other Sales rep's records in their department.

Screenshot of the Row-Level Security configuration showing several rules attached to different security groups, each with a match column and a context-expression value

Performance considerations

Row-level security adds a WHERE clause to every query against the datastore. For tables with millions of rows, make sure the column used in the security rule is indexed — otherwise every read becomes a slow table scan.

Specifically:

  • Index owner_id if you use owner-based rules.
  • Index department (or whatever segmentation column) for department-based rules.
  • Index tenantid for multi-tenant — the platform usually does this automatically when you turn on multi-tenancy.

The override role

Often you want one role (admins, support staff) that can see all rows regardless. Two approaches:

Approach 1: Don't configure a rule for the admin group

If a security group has no row-level rule on a datastore, they see all rows (subject to datastore-level read permission). So just leave the admin group's rule blank.

Approach 2: Configure a permissive rule

Explicit rules can be set to always-true (e.g. 1=1) to grant unrestricted access. Useful when you want the rule list to document the policy explicitly.

Warning: Row-level security applies to the auto-generated forms, browse views, and API. If you write custom SQL via Custom Query Views or in raw PHP, the platform doesn't automatically inject the row-level filter — you're responsible for applying it in your code.

Permission Inheritance

Permissions cascade in well-defined ways in ActiveManage. Understanding the cascade helps you configure efficiently without redundant entries and prevents the most common class of permission bug: "I granted access but the user still can't see it".

The cascade rules in one paragraph

Datastore-level permission is the baseline. Field-level permissions override the baseline only for the fields they explicitly reference. Row-level security applies on top of both — even if you have Read at the datastore level and Read on every field, row-level security still narrows which specific rows you can see. A user's effective permissions are computed by combining (union) every group they belong to.

From datastore to field

A field with no explicit field-permission row inherits the datastore-level permission for the user's groups. Only define field-level permissions where you want to differ from the default.

So if the Finance Team has Read on the Customers datastore, and there's no field-permission row for the Salary field for the Finance Team, the Finance Team can read the Salary field. Add an explicit "No Access" row to hide it for Finance specifically; leave it out to inherit Read.

From group to user

A user's effective permissions are the union of all their security groups' permissions. If any group they're in grants Read, they can read; if any group they're in grants Write, they can write.

Practical consequence: a user in both "Read-Only Staff" and "Editor" groups effectively has the Editor's combined permissions. Don't create a "Restricted Editor" group expecting it to override the broader Editor group's grants — group memberships add, they don't subtract.

The grant-only model

Without any explicit grant, the answer is no. Granting in one group is enough to allow; you don't need to repeat the grant for other groups the user is in.

Three implications:

  • To restrict a user, remove them from the group that grants the access. Don't try to "deny" them in a group they're in.
  • There's no precedence rule — every grant is equal. You don't need to think about "which group wins".
  • Audit is simple: "this user has access because they're in this group" is always the answer. The grant-only model makes audit trails easy.

Three worked-example scenarios

Scenario 1: Adding a new role to existing groups

A user has been promoted from Sales Rep to Sales Manager. They keep their Sales Team group membership (so they continue to see their own old records) and gain the Sales Managers group (which adds visibility into the broader department). The cascade resolves to: their own records (from Sales Team rule) plus the whole department's records (from Sales Managers rule). Both unioned.

Scenario 2: Restricting a previously-permissive user

Someone moved out of a role where they had broad access and should now have narrower access. Don't add them to a "restricted" group — that won't override their existing memberships. Remove them from the group that grants the broader access. Their effective permissions drop immediately on their next request.

Scenario 3: Building hierarchical access

Three groups, each granting progressively more. "All Users" (basic read-only on everything), "Editors" (write access on most things), "Admins" (everything plus delete). Add a user to Editors, they get All Users + Editors permissions. Promote them to Admin, they get all three. No need to remove the lower groups when promoting — the union handles it cleanly.

Diagram showing how permissions cascade from datastore-level down to field-level overrides, with row-level security applying on top, and how multiple group memberships union together to produce effective permissions

Tip: When debugging a permission issue ("why can't this user see X?"), check the user's group memberships first, then check each group's permissions for the relevant datastore and fields. The answer is always somewhere in that chain — there's no fourth layer to forget about.