Team Management
Audience: Support / Management (engineers for the technical section) · Where in app: Sidebar → Team / Members (and Settings → Team) · Plan availability: All plans (seat count is plan-gated)
Team management is where an admin adds members, assigns what each can see and do, groups them into teams and branches, and tracks who's online. Members are UserModel rows linked to the company; access is controlled by the permission array on each user plus user_type — the legacy role/permission layer was removed (see note below).
Members layout (2026-07). The surface is now a three-tab layout — People / Teams / Branches — under
frontend/src/views/members/MembersLayout.vue(List.vue,Teams.vue,Branches.vue, plusCreate.vue/Edit.vue).
What it does
- Add / edit / remove team members.
- Grant granular permissions per member (the UI shows friendly checkboxes; the backend stores permission strings).
- Group members into teams (e.g. Sales, Support) for routing and scoping; group teams into branches (locations).
- Transfer a chat to a whole team — every member is notified, the first to accept claims it (the transfer banner clears for everyone else).
- Track online presence for call/chat routing.
How it works
A new member is created directly (POST /members) with a name, email, password, and a set of permissions — there is no magic-link invite flow for company members (the magic-link/email-campaign ClientInvitationModel is an agency client-outreach tool, not member onboarding; see Agency Overview). Permissions live as an array of strings on the user (users.permissions) and/or via assigned roles (RoleModel). Permission checks run on each request; changing permissions takes effect on the member's next action (the customer docs note updates are effectively immediate).
Configuration & options
Roles & account types
There is no rigid four-role system in code. Two fields matter:
| Field | Values | Meaning |
|---|---|---|
users.type | admin / user | Coarse type (admin = elevated) |
users.user_type | admin / web_owner / member / agency / agency_staff | Account class — web_owner is the company owner/founder, member is an invited team member |
users.permissions[] | permission strings | Direct per-user permission grants |
The "owner" is the web_owner account.
PermissionService, RoleController, and PermissionController were deleted: the seeder gave every role the full permission list, so all 41 permissions.can() guards were tautologies. Real authorization is user_type + the per-user permissions[] array, which is unchanged. populate('roles') was dropped from the bearer strategy (one Mongo query removed per authenticated request); RoleModel docs still exist only for signup assignment. CompanyController.destroy was tightened to super-admin or agency owner/staff over their own company.
Permissions — UI names vs. backend keys (role/permission matrix)
In the app, an admin sees friendly checkboxes when adding/editing a member. The backend stores CRUD-style permission strings. Mapping the two:
| UI permission (what admins see) | Backend permission keys (approx.) |
|---|---|
| Analytics / Reports | (reporting/analytics gates) |
| Live Sessions / Recorded Sessions | session.getAll, session.getOne |
| Chats | chat gates |
| Calls | call gates |
| Leads | lead gates |
| Organization Settings | company.update |
| Widget / Chatbot / AI Agents / Call Settings | feature-config gates |
| Integrations | integration gates |
| Billing | subscription.*, invoice.*, package.* |
| View Team | team.getAll / team.getOne |
| Create Team Members | team.create / users.create |
| Update Team Members | team.update / users.update |
| Delete Team Members | users.delete |
The permission strings are grouped CRUD (getAll / getOne / create / update / delete) across resources: users, pagerule, company, team, session, package, subscription, invoice. New members default to no permissions — they must be granted explicitly. Edit them under Members → Add/Edit Member → Permissions.
The friendly→backend mapping above is approximate. For an exact gate, check the route's permission guard rather than guessing from the label (PermissionService.js no longer exists).
Per-user settings
Each member has: personal working hours (users.working_hours + users.timezone, overlays org hours), notification preferences, profile photo (users.photo_file), profile fields (first_name, last_name, phone, designation, location), OAuth links (google_id, apple_id), and a status of pending / active / blocked. See Account Settings.
Teams (sub-groups) — TeamModel
- Fields:
name,description,members[](user refs),company_id,branch_id(ref →branches, nullable, indexed). - Group members (Sales, Support) and route chats/calls to specific teams via channel settings. Useful for scoping in larger orgs.
- Routes:
POST /teams,GET /teams,GET /teams/:id,PUT /teams/:id,DELETE /teams/:id.
Branches — BranchModel
- Fields:
name,description,company_id(collectionbranches, routes inbranchRoutes.js). A branch groups teams by location/office; teams attach viateam.branch_id. Managed in the Branches tab of the Members layout.
Chat transfer to a team
ChatController transfer accepts transfer_to_team or transfer_to_user. For a team transfer, every team member (minus the transferring agent) gets the pending-transfer notification via their personal socket room (user:<id>); acceptChatTransfer stamps accepted_at/accepted_by — first accept claims the chat and clears the banner for the rest.
Adding & removing members
- Add:
POST /members(name, email, password, permissions).GET /members/allowed-memberschecks the plan's seat cap first. - Block: status →
blocked; can't sign in, history retained. - Delete:
DELETE /members/:_id(requiresusers.delete); chats/calls re-assign to the company with no agent attribution.
Behaviors & edge cases
- Online presence (corrects a common assumption):
users.online_statusis a manual toggle — enumonline/away/offline, updated viaPUT /user/online-statusand stamped onusers.online_status_at. It is not auto-computed from working hours in the core path; a member sets their own availability.last_loginis set at sign-in only. (Working hours still gate widget/routing elsewhere, but the member'sonline_statusitself is user-controlled.) - Owner protection: the
web_ownercan't be deleted via the normal member flow — ownership must be re-assigned manually. Full account deletion is admin-only (DELETE /user/:_id, requiresuser_type === 'admin'). - Permission caching/latency (QA): permission checks are per request; in practice updates apply on the member's next action.
- Seat limits (QA): creating a member when the plan seat cap is hit is blocked — buy a seat add-on or upgrade. See Plans & Subscriptions.
Plan & limits
- Team management is on all plans, but the number of seats is plan-gated (
PackageModel.default_users/max_users, plusadditional_user_pricefor add-on seats). Exact per-tier seat counts — verify against the live packages.
Technical implementation
- Models:
UserModel,TeamModel,BranchModel(RoleModelsurvives only for signup assignment) —backend/app/models/. - Routes:
memberRoutes.js(/members*),teamRoutes.js(/teams*),branchRoutes.js; guards checkuser_type+ the per-user permission strings. - Frontend:
frontend/src/views/members/MembersLayout.vue(People/Teams/Branches tabs),List.vue,Teams.vue,Branches.vue,Create.vue,Edit.vue; data viasrc/composables/useMembersQuery.js. - See backend service.
Related
- Account Settings
- Organization Settings
- Agency Overview (agency staff & client invitations)
- Plans & Subscriptions