Skip to main content
A team is a named group of members in your organization — org-scoped, not tied to any one channel. Two shipped surfaces consume the same roster: the inbox’s Assign to team picker and the routing-rule action assign_team, which spreads inbound conversations across the group instead of leaving them floating in an unassigned bucket. The concept and full endpoint reference live in Roles, teams, and permissions; this page is the operational walkthrough from create-team through to wiring the assign_team routing rule. Everything here is tenant-owned: team definitions, member rosters, and the rules that target them live in your organization’s tenant schema and take effect on the next inbound item. None of these controls change how outbound calls or messages are placed — outbound MT voice and SMS continue to exit exclusively through the Devotel wholesale softswitch. Base path: /api/v1/teams Authentication: Clerk session (Authorization: Bearer <token>) or API key (X-API-Key).

1. What a team is

A team is one row in your organization’s tenant schema: a name (unique per organization, up to 120 characters), an optional description (up to 500), and an active flag. Members are separate rows that link an existing organization user to the team — the team never creates a user; invite the person to your organization first from Settings → Team, then attach their user id. Both consumers share the roster, so the membership you manage on one screen is the pool the routing action picks from and the group a supervisor samples:
  • The dashboard’s Voice → Teams page lists every team in the organization with its name, description, active state, and member count, and opens the create / edit / member dialogs.
  • The inbox’s Assign to team picker reads the same list for manual assignment.
  • The assign_team routing action picks one active member of the roster at assignment time.
  • Supervisor Team Listen (Voice → Monitoring) reuses the roster for group QA sampling.

2. Create a team

From the dashboard, open Voice → Teams and click Create team. Enter a routing-meaningful name (the routing rule references the team by it — “Billing support” reads better in a rule list than “team_1”), an optional description, and set the active flag. Save; the team appears in the list sorted by name. Over the API:
cURL
Create returns 201 with the new team; the shape is { id, name, description, active, created_at, updated_at }. Team names are unique per organization — a duplicate name returns 409 CONFLICT. Each organization is capped at 200 teams; over the cap, creation returns 409 LIMIT_EXCEEDED. Deleting a team returns 204 and removes its member rows with it; a routing rule that still targets the deleted team falls back to the safe behavior in Section 5, so re-point or delete the rule before you delete the team.

3. Manage membership

Members link an organization user id to the team. The user must already belong to your org (invite from Settings → Team first). Adding the same user twice returns 409 CONFLICT; removing returns 204.
cURL
Member listing is keyset-paginated over (created_at, id) — read the opaque cursor from the response’s pagination metadata to fetch the next page, up to 100 rows per page. The response also carries a total count that stops at 1,000 and flags total_capped: true at the ceiling (render “1,000+”). Each member row carries last_assigned_at, the timestamp the routing picker uses for round-robin fairness (Section 4). The roster is additive across teams — removing a user from one team does not affect their membership in any other.

4. Wire the routing rule

Rules evaluate in priority order and AND-combine their conditions; the full condition/action reference is in Omnichannel queue routing. From the inbox routing-rules builder or the API, create a rule whose action is assign_team with the team as its target:
cURL
When the rule fires, the platform picks one active member of the team by a load-balanced ordering:
  1. fewest currently-open conversations assigned to them (open-count ascending),
  2. tie-break on the last-assigned time, oldest first (a member never picked yet wins), then
  3. user id, for a deterministic order on perfect ties.
The conversation is then assigned to that picked user — a real user id, so the inbox assignee, filters, and reporting all render it as a person, exactly like a manual assignment — and the member’s last_assigned_at bumps so the next assignment favors the rest of the roster. If your organization configured per-channel concurrency caps (Settings → Team → Channel concurrency caps), members already at their ceiling on the conversation’s channel are skipped; if every active member is at cap, the conversation is left unassigned rather than overshooting. The assign_team action only considers teams whose active flag is on — a rule pointing at a deactivated team falls through to the safe behavior in Section 5.

5. Empty-team behavior

If the rule targets a team with no active members — the team was deactivated, every member was removed, or the team no longer exists — the action refuses to mis-assign. The conversation is left unassigned and a system-authored internal note is posted on its timeline saying the team-based assignment could not resolve. Fix the root cause and the next inbound item routes normally: reactivate the team, staff it, or re-point the rule at a team that has members.

6. Permissions

  • ReadsGET /api/v1/teams and GET /api/v1/teams/{id}/members admit any member of the organization. The inbox’s Assign to team picker and the Team Listen group list work for agents and supervisors, not just admins.
  • Writes — create, update, delete, and membership changes require the team:invite grant. On the built-in roles that means owner or admin; on a custom role, the role must include the “Invite team members” capability — the check honours the custom role’s grant set strictly, so a custom role without the grant is denied even for a built-in-looking caller.
Every create, update, and membership change is written to your audit log (teams.created / teams.updated / teams.deleted / teams.member_added / teams.member_removed).

7. Worked end-to-end example

Create a team, staff two members, and route inbound billing email to it:
cURL
From here on, an inbound email whose contact carries topic: billing assigns to the least-loaded active member of the Billing support team, and the round-robin marker keeps the pool spread evenly.