# Roles & Permissions

> Granular access control with customizable roles and permissions

Source: https://docs.soosh.io/features/roles-permissions/

Soosh provides a flexible role-based access control (RBAC) system that allows you to define custom roles with granular permissions.

## Overview

The permission system is built around:

- **Permissions**: Fine-grained access controls for specific actions on resources (e.g., `users:write`, `contacts:read`)
- **Roles**: Collections of permissions that can be assigned to users
- **System Roles**: Pre-defined roles (Admin, Manager, Agent) that cannot be deleted

## Workspace components

Platform administrators choose which components a workspace can use under **Platform →
Organizations → organization → Features**. Each component is switched on or off on its own:
for example Contacts without Tags, or Call logs without Outgoing calls. Some components need
another one (Conversation assignment needs Inbox; Keyword replies need the Bot; Customer memory
needs AI replies), and the
editor turns them on or off together after asking. Members, teams, roles, general settings,
billing and audit logs are required and always available. Roles grant actions within the
available components. Access also requires active organization membership and any
assignment or record-visibility rules.

New organizations start with optional components unavailable. Roles, the role editor and
API-key scopes show only the permissions of components the workspace has, plus the governance
permissions every workspace has; role counts count those. Built-in roles are always listed;
a custom role with nothing left says *No enabled permissions*. When a component is taken away,
roles keep their saved grants for it: they give no access, the role editor notes how many are
kept, and saving the role doesn't remove them unless you choose **Remove them** (you must hold
those permissions yourself). Adding the component back makes the kept ones work again, so remove
those you don't want before it's added.
Tenant role administrators can delegate only permissions they hold and cannot edit their own
role. If a role or the workspace's components change while a role is open, saving asks you to
reload it, so no grant is dropped unseen.

Platform services — Organizations, Sign-in (SSO), WhatsApp app and Webhook inbox — are managed only by super administrators. SSO availability is shared across organizations and cannot be disabled through organization allocations or role grants.

## System Roles

Four system roles are created automatically for each workspace:

| Role | Permissions granted |
|------|-------------|
| **Owner** | Every customer permission in the catalogue below, including `billing:write`. Held by exactly one member: the customer named when the workspace was created. Its permissions are fixed and cannot be edited, even by platform staff. |
| **Admin** | Every customer permission except `billing:write`. In workspaces created before owners existed, roles keep `billing:write` until the workspace gets its first owner; that assignment removes it from every role except Owner, so the owner re-approves who can change billing. |
| **Manager** | Everything except: users, roles, SSO settings, API keys, audit logs, organization write/delete/assign (`organizations:read` only), and `teams` write/delete (`teams:read` only). |
| **Agent** | `accounts:read`, `catalogs:read`, `chat:read`, `chat:write`, `chatbot.memory:read/write`, `contacts:read`, `tags:read`, `analytics.agents:read`, `transfers:read/write/pickup`, `canned_responses:read`, `call_transfers:read/write`, `outgoing_calls:read/write`. Note agents deliberately have **no** `call_logs:read` — they see only their own calls. |

> **Note:** System roles cannot be **deleted**. Super admins can edit the permissions of the Admin, Manager
> and Agent roles; nobody can edit the Owner role's permissions. Anyone with `roles:write` can edit
> a system role's description. The name "owner" is reserved for the built-in role.

### Workspace owner

Every action the owner can take is still limited to the modules enabled for the workspace; the
owner never gains platform authority.

- **Only the owner manages administrators.** A role is administrator-level when it holds
  `users:write`, `roles:write` and `organizations:assign` (the built-in Admin role, or any custom
  role with all three). Only the owner, or platform staff, can assign such a role, change or remove a
  member who holds one, or edit an administrator-level role's permissions. Admins still manage
  Managers, Agents and other non-administrator roles.
- **Billing changes are owner-controlled.** Only the Owner holds `billing:write` by default. The
  Owner can delegate it through a custom role (for example, to a finance contact), but only the
  Owner or platform staff can create, edit or assign a role that includes `billing:write`, or change
  or remove a member who holds one. A delegate can change billing but cannot pass it on. Everyone
  with `billing:read` can still view billing.
- **Nobody can demote or remove the owner** through member or user management, and the Owner role
  cannot be assigned there. Platform staff cannot deactivate the owner's account or make it a
  platform administrator while it owns a workspace.
- **The owner can transfer ownership** in **Settings → General → Workspace owner**: choose an active
  member who has set their password, then confirm with your current password. You stay in the
  workspace as an Admin and lose billing control; billing delegates you approved keep it. If you
  sign in with SSO and don't know your password, set one with **Forgot password** first.
- **Platform recovery:** platform staff can change the owner from **Platform → Workspaces →
  workspace → Workspace owner**, recording a confirmation reference and reason after verifying the
  request with the current owner (or the customer's verified business contact). The previous owner
  stays as an Admin. Every ownership change is audited.
- **Workspaces created before owners existed** show **Owner needed** until staff assign one. Until
  then, admins cannot manage other admins; only platform staff can.

## Custom Roles

Create custom roles to match your organization's needs. For example:

- **Support Lead**: Can manage contacts and view analytics, but not manage users
- **Campaign Manager**: Can create/manage campaigns and templates only
- **Read-Only Auditor**: Can view all data but not make changes

### Creating a Custom Role

1. Go to **Settings → Roles**
2. Click **Add Role**
3. Enter a name and description
4. Select permissions from the permission matrix
5. Click **Create**

## Permissions

Permissions follow the format `resource:action`. Available actions are:

| Action | Description |
|--------|-------------|
| `read` | View the resource |
| `write` | Create **or** modify items (there is no separate `create`/`update` — both are `write`) |
| `delete` | Remove items |
| `sync` | Sync from an external source (templates) |
| `execute` | Run the resource (campaigns) |
| `import` | Bulk-import records (contacts) |
| `export` | Bulk-export records (contacts) |
| `pickup` | Claim a queued item (transfers) |
| `assign` | Manage organization members (`organizations:assign`) |

> **Note:** There is **no** `create` or `update` action. Creating a new item and editing an existing one
> both require the `write` action on that resource.

### Available Resources

This is the complete seeded catalogue. `GET /api/permissions` returns the part of it your workspace has: permissions of components it doesn't have, and platform permissions, are left out.

| Resource | Actions | Description |
|----------|---------|-------------|
| `users` | read, write, delete | User management |
| `teams` | read, write, delete | Team management |
| `roles` | read, write, delete | Role management |
| `settings.general` | read, write | General organization settings |
| `settings.calling` | read, write | Organization calling settings |
| `settings.chatbot` | read, write | The bot's messages, session timeout and sessions, and AI settings while the AI component is on |
| `chatbot.routing` | read, write | Agent routing settings: queue pickup, returning customers, current conversation only |
| `chatbot.hours` | read, write | Business hours and the out-of-hours reply |
| `chatbot.sla` | read, write | SLA targets, escalation and client inactivity |
| `accounts` | read, write, delete | WhatsApp account settings |
| `catalogs` | read, write, delete | Product catalogs and products |
| `templates` | read, write, delete, sync | Message template management; `sync` pulls templates from Meta |
| `flows.whatsapp` | read, write, delete | WhatsApp Flows (interactive forms) |
| `flows.chatbot` | read, write, delete | Chatbot flow management |
| `campaigns` | read, write, delete, execute | Campaign management; `execute` starts/retries a send |
| `chatbot.keywords` | read, write, delete | Chatbot keyword auto-reply rules |
| `chatbot.ai` | read, write, delete | Knowledge base (AI contexts). AI settings use `settings.chatbot` while the AI component is on |
| `chatbot.memory` | read, write | View and edit customer memory beside a chat |
| `chat` | read, write | View conversations (`read`) and send messages (`write`) |
| `chat.assign` | write | Assign conversations to agents |
| `contacts` | read, write, delete, import, export | Contact management and bulk import/export |
| `tags` | read, write, delete, import, export | Contact tag management |
| `analytics` | read, write, delete | View the analytics dashboard (`read`); `write`/`delete` govern dashboard widgets |
| `analytics.agents` | read | Agent performance analytics |
| `transfers` | read, write, pickup | Chatbot-to-agent transfer queue |
| `webhooks` | read, write, delete | Outbound webhook configuration |
| `api_keys` | read, write, delete | API key management |
| `canned_responses` | read, write, delete | Saved reply templates |
| `custom_actions` | read, write, delete | Custom action buttons |
| `organizations` | read, assign | Tenant member visibility and assignment |
| `call_logs` | read | Voice call history |
| `ivr_flows` | read, write, delete | IVR call flow definitions |
| `call_transfers` | read, write | Voice call transfers |
| `outgoing_calls` | read, write | Outbound calling |
| `audit_logs` | read | Audit trail (read-only) |

> **Note:** `settings.notification` and the `settings.chatbot.*` sub-resources
> (`.messages`, `.agents`, `.hours`, `.sla`, `.ai`) appear in **audit log entries** as resource
> labels, but they are not grantable permissions and are never seeded.

> **Careful:** `GET /api/org/settings` requires authentication only. `PUT /api/org/settings` checks
> `settings.general:write` or `settings.calling:write` for the fields it changes. The Meta app
> used for Embedded Signup is platform-wide and managed by super admins only, via
> `GET`/`PUT /api/admin/platform/meta-app`.

### Permission Matrix

When creating or editing a role, you'll see a permission matrix grouped by module, with resources inside each group. Only the workspace's components are listed:

```
Users
  ☑ Read users     (users:read)
  ☑ Write users    (users:write — create or edit)
  ☐ Delete users   (users:delete)

Contacts
  ☑ Read contacts     (contacts:read)
  ☑ Write contacts    (contacts:write)
  ☐ Delete contacts   (contacts:delete)
  ☐ Import contacts   (contacts:import)
  ☐ Export contacts   (contacts:export)
```

## Assigning Roles to Users

1. Go to **Settings → Users**
2. Click on a user or create a new one
3. Select a role from the dropdown
4. Save changes

> **Tip:** Permissions are read from the database on every request, so role and membership changes take
> effect on the next request without logging out.

## Multi-Organization Access

Users can belong to multiple organizations, each with a different role. The organization switcher appears in the sidebar for users who are members of more than one organization.

### Organization Switching

When switching organizations:

1. New JWT tokens are issued scoped to the target organization via `POST /api/auth/switch-org`
2. The user's role and permissions are loaded from their membership in the target organization
3. All data views refresh to show the selected organization's data
4. The selected organization persists across page navigation

### Cross-Organization Members

Users added to an organization from another org appear with a **Member** badge in the user list. Members have limited management:

- **Role updates** — only the org-specific role can be changed
- **Removal** — removes the user from the organization without deleting their account
- Other fields (email, password, name, active status) are managed in the user's home organization

### Closed workspaces

Platform support can close a workspace, for example while an account is suspended. Closing deletes
nothing: data, members, roles, enabled modules, the wallet and history are all kept.

While a workspace is closed:

- Members see **Workspace closed — contact support** instead of the workspace, and can still open
  their other workspaces from that page or the switcher, where the closed one is marked **Closed**.
  Signing in takes you to another workspace you belong to, if you have one.
- API keys for the workspace stop working, and nothing is sent from it: no messages, campaigns,
  chatbot or AI replies, calls, outgoing webhooks, store automations or auto-recharges.
- Messages and calls that arrive are acknowledged to WhatsApp but not kept, so they don't appear
  after it reopens. Delivery and read receipts for messages already sent are still recorded.

When support reopens the workspace, everything works again, but nothing restarts on its own:
campaigns that were running or scheduled stay **Paused** until you resume them, and store
automations only message orders and checkouts from after the reopen.

### Deleted workspaces

Platform support can also delete a workspace, for example when an account ends. A deleted
workspace is closed as well, and it disappears for its members: it is no longer listed in the
switcher, and anyone signed in to it is signed out. Nothing is removed, though: support can
restore the workspace with all its data, members and history. It comes back closed until support
reopens it. Its WhatsApp numbers can't be connected to another workspace while it is deleted.

The workspace Owner can't close or delete their own workspace; contact support.

### Super Admin

Super admins have additional privileges:

- Access to all organizations, even without explicit membership
- Can switch to any organization using the `X-Organization-ID` header
- Can create new organizations

> **Careful:** The `X-Organization-ID` header switches the org for a single request. A super admin may name any
> existing organization; anyone else may only name an org they hold a `user_organizations`
> membership in. An unusable value is ignored silently — the request falls back to the org in the
> caller's JWT rather than failing. `POST /api/auth/switch-org` is the durable alternative: it
> re-issues the auth cookies scoped to the new org, so every later request follows without the
> header.

## UI Behavior

The frontend dynamically adapts based on user permissions:

### Sidebar Menu

Each route declares the resource it needs in its `meta.permission`, and the router hides the menu
item and blocks navigation when the user lacks `<resource>:read`:

| Area | Resource |
|---|---|
| Dashboard / Analytics | `analytics` |
| Agent Analytics | `analytics.agents` |
| Chat | `chat` |
| Contacts | `contacts` |
| Tags | `tags` |
| Templates | `templates` |
| WhatsApp Flows | `flows.whatsapp` |
| Campaigns | `campaigns` |
| Chatbot settings & builder | `settings.chatbot` (agent routing, business hours and SLA tabs need their own: `chatbot.routing`, `chatbot.hours`, `chatbot.sla`) |
| Keyword rules | `chatbot.keywords` |
| Chatbot flows | `flows.chatbot` |
| AI contexts | `chatbot.ai` |
| Customer memory | `chatbot.memory` |
| Transfer queue | `transfers` |
| General settings | `settings.general` |
| Accounts | `accounts` |
| Catalogs | `catalogs` |
| Canned Responses | `canned_responses` |
| Users | `users` |
| Roles | `roles` |
| Teams | `teams` |
| API Keys | `api_keys` |
| Webhooks | `webhooks` |
| SSO | super admins only |
| Custom Actions | `custom_actions` |
| Audit Logs | `audit_logs` |
| Call Logs | `call_logs` |
| IVR Flows | `ivr_flows` |
| Call Transfers | `call_transfers` |

### Page Access

If a user tries to access a page they don't have permission for, they are redirected to the first accessible page.

### Action Buttons

Create, edit, and delete buttons are shown only if the user has the corresponding permission. For example, the "Add User" button only appears if the user has `users:write` permission.

## API Authorization

All API endpoints check permissions before processing requests:

```bash
# Returns 403 if user lacks users:read permission
GET /api/users

# Returns 403 if user lacks campaigns:write permission
POST /api/campaigns
```

### Response for Unauthorized Access

A missing permission returns `403` with a generic message — the specific `resource:action` is not
disclosed. An unauthenticated request returns `401 Unauthorized` instead.

```json
{
  "status": "error",
  "message": "Insufficient permissions",
  "data": null
}
```

## Best Practices

1. **Principle of Least Privilege**: Assign only the permissions users need
2. **Use Custom Roles**: Create roles that match job functions rather than assigning system roles
3. **Regular Audits**: Periodically review role assignments and permissions
4. **Document Roles**: Use meaningful names and descriptions for custom roles
