# Prepare team access and check what a colleague can actually do

![Sales configured to grant the LeadEditor role.](assets/appmint-roles/25-sales-role-assignment.png)
*1 — The group represents a job in the business. 2 — Its selected role describes the intended access. Keep this example group empty until the custom-role issue below is resolved.*

**Who:** owners and administrators onboarding colleagues. **Time:** 45–60 minutes, plus a separate colleague test. **Level:** beginner configuration, developer checks afterwards. **Product:** Appmint Studio Manager. **Checked:** current local Studio 0.6.2 and production 0.6.1, 17 September 2026. **Example:** Cedar & Form, owner Jordan Morgan and prospective colleague Maya Ito. Maya Ito is a staff example, distinct from client Maya Bennett in the CRM course.

**Current limitation:** the role and group settings save, but the installed AppEngine permission resolver fails on the custom `LeadEditor` name. Changing group membership also produced **User should be updated using user api** after the group record had already saved. The walkthrough shows both issues. Do not use the custom example as a working staff grant until those paths have been fixed and tested.

## What you will have at the end

You will understand the difference between permission verbs, visible screens and group membership; create an explicit role definition; save two empty job groups; prepare an invitation; and know how to test a grant, a refusal and a later change.

You will also learn why an administrator’s broad access is unsuitable for testing a colleague’s restrictions, why a saved group card can disagree with the user record, and how to report the exact failed action without repeatedly resubmitting it.

The recorded result is configuration and a membership rehearsal. Invitation delivery, acceptance, a restricted colleague session and an approval decision still require a controlled second account.

## What you need

- Your owner account from [Welcome to Appmint](appmint-welcome.md). Keep that account in its normal browser.
- A second browser profile and an accessible email inbox controlled by the colleague or your test team.
- A harmless enquiry record from [the CRM course](appmint-turn-an-enquiry-into-a-project.md) for a later edit test.
- A short description of the job. In this example: “Follow up enquiries; read, create and update lead records; do not administer the team.”

The primary screenshots below use **0.6.2**. Production **0.6.1** currently shows older forms. The differences are explained near the relevant steps; do not combine the two interfaces into one click sequence.

## The story

Jordan wants Maya Ito to work the enquiry pipeline. Giving her an owner login would make the first task easy but would also let her administer unrelated parts of the company. Instead, Jordan defines the intended job, puts that definition in a Sales group and tests the access from Maya’s own session before relying on it.

```mermaid
flowchart LR
    A[Job: follow up enquiries] --> B[Role: actions and screens]
    B --> C[Group: Sales]
    C --> D[Named colleague]
    D --> E[Test permitted work]
    D --> F[Test refused work]
    E --> G[Recheck after any access change]
    F --> G
```

There are three different questions to answer:

| Question | Control | Example |
| --- | --- | --- |
| What actions are intended? | Role content/component permissions | Read, Create, Update |
| Which screens should be offered? | Role Menu access | CRM → Leads |
| Who holds that job? | Group membership | Maya belongs to Sales |

A menu restriction is not a per-collection security rule. A role name such as LeadEditor is only a name; it does not automatically scope every server request to lead records.

## Part 1 — Define the role before adding people

### 1. Open Role & Permission

Expand the chevron beside **Configuration**, then select **Role & Permission**. Selecting the group name itself opens the Configuration dashboard; the chevron exposes its child links.

The **Role & Permission Manager** lists system roles and custom roles. Select **New Role**.

![The current custom role form.](assets/appmint-roles/21-current-new-role.png)

If the sidebar is reduced to icons, select its **Expand sidebar** control first. The course’s local session initially used a narrow window and this collapsed view; it was not a missing Configuration module.

### 2. Give the role a stable name and a useful description

Enter:

| Field | Value |
| --- | --- |
| Name | `LeadEditor` |
| Description | `Enquiry team: read, create and update records; Leads screen only.` |

The name is an identifier. Use letters, numbers, hyphens or underscores, and avoid spaces. The description tells a future administrator why the role exists. Changing the description is easier than discovering later that two similarly named roles mean different things.

### 3. Select the record actions

Under **What this role can do with records**, select **Read**, **Create** and **Update**. Leave **Delete**, **Review** and **Approve** unselected.

- **Read** supports opening records.
- **Create** supports adding a new enquiry.
- **Update** supports correcting or progressing an existing enquiry.

Leave **What this role can do on pages** clear for this example. Page component actions are a different responsibility from following up an enquiry.

**You should see:** **3 of 6 granted** for record actions. This is the intended role definition; it does not yet establish that the server resolves a custom role correctly.

### 4. Select the Leads screen explicitly

In **Menu access**, keep the **Appmint** tab selected. Find the **CRM** section and select **Leads**, whose displayed route is `/leads`.

Do not select the whole CRM group when the intention is one screen. The panel also has a **Business Made** tab because a role record can carry menus for both applications. Leave BusinessMade choices out of this Appmint example unless the job needs them.

![Role settings with explicit menu choices.](assets/appmint-roles/22-current-role-ready.png)

> **Three different menu states:** **Allow everything** means unrestricted menus; selecting specific entries creates an allow-list; **No access** is the explicit no-screen choice. Clearing every selected link is not a reliable way to mean “deny everything”. Read the panel’s summary before saving.

### 5. Save the role, then reopen it

Select **Create role** at the bottom. After saving, reload the page and select **LEADEDITOR** under **CUSTOM ROLES**. Read back its name, record actions and menu selection.

![The custom role reopened after reload.](assets/appmint-roles/23-current-role-reloaded.png)

**You should see:** an editable custom role with the saved choices. Keep it unassigned while you complete the server check in Part 4.

**If production shows a different form:** 0.6.1 opened a floating **User Role** form with **Name**, **Description**, **Type**, **Content**, **Component**, **Menu Include** and **Menu Exclude**. Its menu options were older categories such as **CRM**, not the current `/leads` entry. The production rehearsal saved read/create/update with CRM, then reopened it. That broader category is not equivalent to a Leads-only menu.

![Production 0.6.1’s older role form.](assets/appmint-roles/04-role-ready.png)

Use the instructions matching your build. If precise menu selection is unavailable, do not silently substitute a broader category while telling the colleague that only Leads is exposed.

**Try it:** explain the difference between Update and Delete using a real task: correcting a phone number versus removing a lead record.

**Check yourself:** if the role says LeadEditor but its menus include every CRM screen, is it Leads-only? No. Read the stored actions and menu entries, not the name.

## Part 2 — Put the job in a group

### 1. Create Sales

Open **Configuration → User, Group**. Select **Groups** in the page header to reach **User Groups**, then **Create Group**.

In **New group**, enter:

| Field | Value |
| --- | --- |
| Name | `Sales` |
| Description | `Cedar & Form enquiry team` |
| Password policy | `Org default` |

Expand **Roles this group grants** if necessary and select **LeadEditor** only. Leave **Members** empty for now. Select **Create group**.

![The group’s identity and selected role.](assets/appmint-roles/25-sales-role-assignment.png)

Do not add Owner, ConfigAdmin or another broad role to make a failing custom role “work”. That changes the access you are trying to establish.

### 2. Reload and inspect the group

Reload Studio, return to **Groups** and locate **Sales**. The card should show **Roles 1**, **LeadEditor**, and no members.

![Sales and its saved role after reload.](assets/appmint-roles/26-sales-group-reloaded.png)

The card in this build displayed **No description provided**, even though the description remained in the edit form. Open the editor before assuming text was lost and creating another group.

Once created, the current editor disables the group name and explains **Users are linked by this name — it cannot change**. Choose a durable job name such as Sales rather than a person’s name.

### 3. Prepare a separate website group

Create a second empty group:

- **Name:** `Website Editors`
- **Description:** `Cedar & Form website editing team`
- **Roles this group grants:** **Publisher**
- **Password policy:** **Org default**

Save and reload. The card should show Publisher and zero members.

![Website Editors saved as a separate empty group.](assets/appmint-roles/27-website-group-reloaded.png)

**Publisher is broad.** The installed permission resolver gives it read, create, update, review and delete actions. Its built-in menu coverage extends beyond a single website page. This group illustrates a distinct responsibility; it is not a narrowly scoped “may edit one homepage” grant. Review that scope before assigning a colleague.

**Production difference:** the older Create/Edit Group dialog showed name, description, colour and icon, without this role picker. Its separate **Permissions** action offered direct verb checkboxes. Do not treat that older panel as proof that the current server has linked the intended role to the group.

### 4. Understand how membership is added

On **Sales**, select **Add User**. The current editor opens **Edit Sales** with **Members** and **Add people**. Search for an existing user by name or email, select their checkbox, then select **Add 1**. This stages the person in the Members list. Select **Save group** to persist the change.

The recorded rehearsal used the existing owner Jordan, then removed that membership afterwards. Jordan’s Owner permissions remained in place throughout; this was a group-list test, not a restricted-user test.

![Jordan staged as a member of Sales.](assets/appmint-roles/29-add-existing-member.png)

### 5. Handle the partial-save error carefully

Saving the membership displayed:

> User should be updated using user api

The group’s member list nevertheless contained Jordan after reload.

![Membership read back after reload.](assets/appmint-roles/30-member-reloaded.png)

This matters because membership has two representations: the group lists its people, and the user record carries group names used in permission calculation. A saved group card alone does not mean both were updated successfully.

If this error appears, reload and inspect the group and user before retrying. Report the group name, affected user and exact message. Have the user test their permitted work only after the relationship has been repaired through the supported user path. Do not keep pressing Save or tell them the grant is complete because their avatar appears on the card.

### 6. Remove a rehearsal member

In **Edit Sales → Members**, use **Remove from group** beside the person, then **Save group**. Reload and inspect the list again.

The rehearsal removal produced the same partial-save error. After reload, Sales returned to zero members and retained LeadEditor. Jordan’s existing administrator groups were not removed.

![The actual error during membership removal.](assets/appmint-roles/31-membership-save-result.png)

![Sales restored to an empty member list.](assets/appmint-roles/32-empty-group-restored.png)

A person shown as a member **via** a role is different from someone explicitly added to the group. The current editor does not offer the same direct removal for inferred membership. Identify the actual grant instead of trying to remove a display entry repeatedly.

**Try it:** inspect an empty group and a seeded administrator group without changing either. Identify the role granted and the reason each listed person belongs.

**Check yourself:** did Jordan becoming a Sales member prove that Maya will be restricted to Leads? No. Jordan still held Owner access, and the user-mirror update failed.

## Part 3 — Prepare a real colleague’s invitation

### 1. Open Send Team Invitation

In **User Management**, select **Invites**, then **Send Invitation**. The drawer is **Send Team Invitation**.

Enter the colleague’s **First Name**, **Last Name** and **Email Address**. The example uses Maya Ito. Replace the fictional email in the screenshot with an address the intended colleague controls.

Under **Assign to Groups**, choose the job group only after its grants have passed the checks in Part 4. The prepared screenshot shows Sales to explain the control; this invitation was cancelled without sending because the custom role was not ready.

![Prepared invitation, not submitted.](assets/appmint-roles/28-prepared-invitation.png)

Use **Personal Message (Optional)** to give context, for example: “Welcome — you will be working the enquiries board.” The message complements access; it does not grant it.

### 2. Review the delivery and access together

The form offers **Send welcome email with login instructions**. Do not treat that checkbox as a proven “create silently” switch: the inspected invitation service still creates an invitation notification without consulting that flag. Use an authorised recipient when you eventually send.

Before selecting **Send Invitation**, confirm the person, address and intended groups. An invitation is a future grant when accepted. Adding several groups “for later” can grant more than the current job needs.

The expected follow-up is a pending entry in **Invitations**, the colleague’s acceptance, then their appearance under **Team Members**. That sequence still needs a mailbox-backed rehearsal for this course. A prepared drawer is not a sent or accepted invitation.

### 3. Let the colleague establish their own session

Use the link provided by the actual invitation email. Keep its token private. The invitation completion flow and fields must match the build being used; do not manufacture a URL from an example token.

Have the colleague sign into the business in their separate browser and confirm their displayed identity. Keep your owner session available for administration. If they land in the wrong company, sign out and use the intended company’s organisation ID.

**Try it:** read the invitation aloud without sending it. Can the recipient tell which business invited them and what job they are joining?

**Check yourself:** can the person’s welcome message compensate for assigning Owner accidentally? No. Access comes from the actual groups and roles.

## Part 4 — Test the grant before relying on it

### The custom-role check found a blocker

The current role editor stores custom choices successfully, but AppEngine’s installed permission helper resolves a fixed set of built-in role names. Executing that helper with `LeadEditor` produced:

> Cannot read properties of undefined (reading 'content')

The same check resolved User and Publisher successfully. Keep the custom Sales group empty until the backend resolves saved custom roles and the staff sign-in/edit sequence passes. Adding a broad built-in role is not a repair for this narrow job definition.

The [recorded resolver results](assets/appmint-roles/permission-resolver-check.json) describe the exact check. This was a local installed-function test, not a production login failure attributed to Maya.

### Test with the colleague’s account

Once the custom-role and membership paths are corrected, run these tasks in the colleague’s own session:

1. Open **CRM → Leads** and find the agreed training enquiry.
2. Change one harmless, reversible detail, save, reload and read it back. Opening the screen alone does not test Update.
3. Check that a Delete action is unavailable or refused under the intended role. Do not use a real customer record for a destructive test.
4. Try the copied **Role & Permission** page address directly. A hidden sidebar item should not be your only access check.
5. Ask a developer to verify server behaviour for the relevant record operations. A menu refusal and an API permission refusal are different checks.

The current UI has a **Not available for your role** screen and an **Ask for access** flow in source. Capture the actual refusal when the second session is available; this manuscript does not claim it has already appeared for Maya.

### Recheck after changes

Use a fresh sign-in when validating a changed grant. Server permissions are calculated during authentication; the menu layer also fetches role/group information. A page reload can therefore change what the sidebar shows without establishing that every server permission has refreshed.

If access still differs from the intended job after a fresh sign-in, inspect every group and role the person holds. The effective result combines grants, and Owner/all-access roles override an otherwise narrow menu configuration. Removing Sales does not remove access independently granted by another group.

**Check yourself:** why test a direct URL and an actual save? The first checks the application’s screen gate; the second exercises the business operation and server permission path.

## Part 5 — Handle requests and ongoing access

### Requests belong to the work being requested

A colleague who needs website access can use **Ask for access** on the relevant refusal screen when the access-request workflow is enabled. As owner, open **App Root → Approvals**.

![The owner’s Approvals page.](assets/appmint-roles/36-approvals.png)

The page has **Waiting on you**, **My requests**, **Notices** and **Decided**. The captured account had no waiting request. No approval was submitted.

When a real request arrives, read what the person is trying to do and choose a group that actually matches it. Website Editors/Publisher is not an appropriate answer to a request for Configuration administration, and its scope is broader than one page. Granting an unrelated group may mark a request decided without enabling the requested task.

The workflow and decision must be tested with the requesting person’s new session. See [Automate a business handoff](appmint-automate-a-business-handoff.md) for the workflow side of approvals.

### Recognise devices and access cards

In **User Management → Devices**, compare the device, person, status and recent activity before using any trust or block action. The recorded page showed one recognised Chrome device for Jordan; no trust or block setting was changed.

![Device Management and its status filters.](assets/appmint-roles/33-devices.png)

Use **Cards** to open **Access Cards**. Card issuance, encoding and revocation affect a physical login route and belong with the employee/device setup. The screen was inspected, but no card was issued or written during this course.

![Access Cards entry.](assets/appmint-roles/34-access-cards.png)

### Keep password policy separate from job permissions

Open **Configuration → Password Policy** to inspect the available policies. The current screen is a data table, including the label **MIN LENGHT**. The training company had no custom policy records.

![The PasswordPolicy table.](assets/appmint-roles/35-password-policy.png)

A group’s **Password policy** selector controls which policy it references; it does not add CRM or website permissions. Leave **Org default** while learning the access model, then define company requirements deliberately.

### Do not confuse menu changes with disabling an account

For a departing colleague, review all their groups, direct grants, active sessions, keys and physical access routes. Removing one menu entry does not disable their account. Follow the actual account-removal or lockout process available on your build, and verify the resulting sign-in behaviour separately.

For API work, use [the AppEngine client course](appengine-build-a-connected-web-or-mobile-client.md). Do not assume a key’s displayed scope chips provide stricter enforcement than its creator’s effective permissions.

## If something goes wrong

| Symptom | Check and next action |
| --- | --- |
| Sidebar shows only icons | Use Expand sidebar; then expand Configuration with its chevron. |
| Role form looks like a generic data editor | You may be on production 0.6.1. Do not substitute its broad CRM category for the current Leads path. |
| A role shows every menu | Inspect Allow everything versus explicit entries. Use No access for a deliberate no-screen role. |
| New group is missing immediately after create | Reload and search by name before creating another. |
| Group card says No description provided | Reopen Edit; the description persisted despite that card text in the rehearsal. |
| User should be updated using user api | Group and user membership may disagree. Read both back and repair the user path before treating access as granted. |
| Custom role causes permission calculation failure | The installed helper failed on LeadEditor. Keep it unassigned and report the custom-role resolution path. |
| Owner still sees everything | Owner is an all-access role; use a separate colleague session for restriction tests. |
| Changed access behaves inconsistently | Use a fresh sign-in, then inspect every applicable group/role and the actual server operation. |
| No invitation arrives | Check the exact recipient and pending record. Do not repeatedly resend or expose the invitation token in a report. |
| Approval did not enable the requested task | Check whether the chosen group actually grants that task, then verify the colleague’s session. |

## What happened behind the scenes

<details>
<summary>Developer evidence and implementation pointers</summary>

The current role editor saves `userrole.data.permissions`, including content/component verbs and menu paths. Group records store role names and member emails. The group editor writes the group first, then attempts to mirror membership to user records through a generic repository save. That latter call produced the observed user-API error.

`UsersService.getUserPermissionsAndRoles` reads `user.data.groups`; `getGroupPermissions` calls the SDK’s `getPermissionEffective` with each group’s role names. The installed SDK’s `getPermissionRoleEffective` looks up its fixed permission object. It does not load the saved custom userrole definition, and dereferencing the missing custom entry produced the captured TypeError.

The client’s menu logic separately reads saved roles and group membership. It handles explicit no-access, customer/guest restrictions and all-access roles. This is why a correct-looking menu is not sufficient to certify server grants.

Relevant source paths relative to projects:

- `websitemint/packages/ui/src/components/configuration/role-permission/role-form.tsx`, `menu-access-panel.tsx`, `use-allowed-menu.ts`
- `websitemint/packages/ui/src/components/user-management/group-editor.tsx`, `modern-invitations.tsx`, `user-management-store.ts`
- `websitemint/packages/ui/src/components/approval/request-access.tsx`
- `appengine/src/users/users.service.ts` and `users/auth/jwt.auth.guard.ts`
- Installed `appengine/node_modules/@jaclight/dbsdk/src/types/role-type.ts`
- `appengine/src/approval/approval.service.ts`

No product source was changed for this documentation task. The stored roles/groups are tutorial records, not a backend fix.

</details>

## Where next

Continue with [Appmint Mobile daily work](appmint-mobile-welcome-and-daily-work.md) and [employee phones and mobile CRM](appmint-mobile-employee-phones-and-crm.md). For the HR side of the same person, use [BusinessMade employee onboarding](businessmade-hire-and-support-an-employee.md).

**Evidence:** production 0.6.1 org `learnmu4zs1td`; current local 0.6.2 org `learnmu4qn1ha`, Jordan Morgan. Role and group creation/readback, invitation preparation without sending, reversible owner membership addition/removal with partial-save error, and read-only Devices/Cards/Password Policy/Approvals views were exercised. Final Sales and Website Editors groups have zero members. No colleague invitation delivery/acceptance, restricted-user login, server denial, approval or card/device action was completed. [Capture ledger](assets/appmint-roles/evidence.json); [permission helper results](assets/appmint-roles/permission-resolver-check.json); [companion-video guide](production/appmint-roles-groups-and-permissions.md).
