Skip to main content

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, plus Create.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:

FieldValuesMeaning
users.typeadmin / userCoarse type (admin = elevated)
users.user_typeadmin / web_owner / member / agency / agency_staffAccount class — web_owner is the company owner/founder, member is an invited team member
users.permissions[]permission stringsDirect per-user permission grants

The "owner" is the web_owner account.

Legacy roles/permissions layer removed (2026-07-20)

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 Sessionssession.getAll, session.getOne
Chatschat gates
Callscall gates
Leadslead gates
Organization Settingscompany.update
Widget / Chatbot / AI Agents / Call Settingsfeature-config gates
Integrationsintegration gates
Billingsubscription.*, invoice.*, package.*
View Teamteam.getAll / team.getOne
Create Team Membersteam.create / users.create
Update Team Membersteam.update / users.update
Delete Team Membersusers.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.

note

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 (collection branches, routes in branchRoutes.js). A branch groups teams by location/office; teams attach via team.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_byfirst accept claims the chat and clears the banner for the rest.

Adding & removing members

  • Add: POST /members (name, email, password, permissions). GET /members/allowed-members checks the plan's seat cap first.
  • Block: status → blocked; can't sign in, history retained.
  • Delete: DELETE /members/:_id (requires users.delete); chats/calls re-assign to the company with no agent attribution.

Behaviors & edge cases

  • Online presence (corrects a common assumption): users.online_status is a manual toggle — enum online / away / offline, updated via PUT /user/online-status and stamped on users.online_status_at. It is not auto-computed from working hours in the core path; a member sets their own availability. last_login is set at sign-in only. (Working hours still gate widget/routing elsewhere, but the member's online_status itself is user-controlled.)
  • Owner protection: the web_owner can't be deleted via the normal member flow — ownership must be re-assigned manually. Full account deletion is admin-only (DELETE /user/:_id, requires user_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, plus additional_user_price for add-on seats). Exact per-tier seat counts — verify against the live packages.

Technical implementation

  • Models: UserModel, TeamModel, BranchModel (RoleModel survives only for signup assignment) — backend/app/models/.
  • Routes: memberRoutes.js (/members*), teamRoutes.js (/teams*), branchRoutes.js; guards check user_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 via src/composables/useMembersQuery.js.
  • See backend service.