Security & admin

Roles, permissions & assigned-only agents

One role per agent, wildcard permission matching, and a restricted scope enforced in the query rather than hidden in the interface.

Leodesk uses straightforward role-based access control: a role holds a set of permission strings, each agent holds one role, and every protected route checks the permission it needs. The part worth reading carefully is assigned-only visibility, because it is enforced in the database query rather than in the interface.

The roles you start with

Signing up seeds three roles and makes the person who signed up an Admin:

  • Admin — holds *, which matches every permission, including managing agents, roles, settings and billing.
  • Agent — holds ticket.* and chat.*. A full support agent who can see and act on every ticket in the workspace.
  • Developer — holds ticket.read, ticket.viewAll and ticket.note. Can read every ticket and leave internal notes, but cannot reply to customers.

Those three cover a surprising number of teams. Build custom roles under Settings → Roles & permissions when they do not — note that custom roles are a Scale plan feature, as is the audit log.

The permission catalog

  • ticket.read — open and read tickets
  • ticket.reply — send replies to customers
  • ticket.note — post internal notes
  • ticket.assign — change a ticket's assignee
  • ticket.delete — delete tickets
  • ticket.viewAll — see every ticket in the workspace, not only their own
  • chat.* — handle live chat conversations
  • reports.view — workspace-wide analytics; without it an agent sees only their own statistics
  • agents.manage — invite, edit and deactivate agents
  • settings.manage — workspace settings, channels, automations and SLA policies

How wildcards match

A permission check passes in one of three ways: the role holds *; the role holds the exact permission; or the role holds the group wildcard for it, so ticket.* satisfies ticket.read, ticket.reply and every other ticket. permission. Wildcards are one level deep — there is no partial matching beyond the group prefix.

The practical consequence is that ticket.* also grants ticket.viewAll. That is exactly why a restricted role has to be built up from granular permissions rather than by starting with the wildcard and trying to take one away.

When a request fails a check, the API returns a 403 naming the missing permission, so a report of "I get an error on this button" usually answers itself.

Assigned-only agents

Sometimes you want an agent — a contractor, an outsourced tier one, a brand-new hire — to work only the tickets assigned to them and see nothing else. In Leodesk that is not a separate mode or a toggle. It is the absence of one permission: omit ticket.viewAll and the role becomes assigned-only.

To build one: create a role with ticket.read, ticket.reply and ticket.note (add chat.* if they take chats), leave ticket.viewAll unticked, save, and assign it in the agent's edit dialog.

Why this holds up

The restriction is applied as a filter on the ticket query itself, not as a condition on what the interface chooses to draw. Any request from an agent without ticket.viewAll is constrained to tickets where they are the assignee — the ticket list, the unread and view counts, opening a ticket directly by its id, and every mutation: replying, noting, tagging, reassigning, resolving, deleting.

An assigned-only agent who takes a colleague's ticket URL and pastes it into their browser does not get a hidden ticket they can still act on. They get nothing, because that ticket was never in the result set. The same is true of a request made outside the interface entirely.

The filter also fails closed. If a session cannot resolve an agent id for any reason, the query is given a sentinel value that no ticket carries, so a broken session sees zero tickets rather than everyone's. The same principle governs workspace scoping: a request whose workspace cannot be resolved matches nothing, never everything.

The one thing to remember is that the seeded Agent and Developer roles both include ticket.viewAll — Agent through its ticket.* wildcard, Developer explicitly. Assigned-only is a role you deliberately create, not a default you inherit.

Three roles worth copying

  1. Contractor

    ticket.read, ticket.reply, ticket.note. No ticket.viewAll, no reports.view. They see their queue, answer it, and cannot browse the rest of your customer conversations or your team's numbers.

  2. Engineer on rotation

    ticket.read, ticket.viewAll, ticket.note. They can investigate anything and leave findings on the thread, but customer-facing replies stay with support. This is the seeded Developer role.

  3. Support lead

    ticket.*, chat.*, reports.view, agents.manage. Everything operational, without settings.manage — so they run the desk without being able to reconfigure email channels or billing.

Beyond roles

Every workspace is isolated from every other one, and every list endpoint is workspace-scoped before permissions are even considered. Roles govern what an agent can do inside their own workspace; they are not the mechanism that stops one workspace seeing another's data.

On plan limits: the Free plan allows three agent accounts and one mailbox. Custom roles and the audit log both require Scale. If you are on Free and need a fourth agent, that is a plan change rather than a permission problem.

New to the workspace? Start with the quickstart, then set up outbound email and inbound mail.