message.comDevelopers

Agents Live

Agents are the team members who handle conversations. Every agent has a role (admin, supervisor, agent), a channel scope, and zero or more department memberships. This API covers invite, edit, and deactivate. There is no direct-create or hard-delete endpoint.

List agents

GET/api/v1/agentsAuth: Bearer

Cursor-paginated list of agents in the workspace. Optional filters by role, status, and departmentId.

Code samples

cURL
curl 'https://app.message.com/api/v1/agents' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/agents', {
  headers: { Authorization: 'Bearer ' + token }
});
const { agents } = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/agents",
    headers={"Authorization": f"Bearer {token}"},
)
agents = r.json()["agents"]
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/agents")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{token}"
  http.request(req)
end
agents = JSON.parse(res.body)["agents"]
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$agents = json_decode(file_get_contents("https://app.message.com/api/v1/agents", false, $ctx), true)["agents"];

Get one agent

GET/api/v1/agents/:idAuth: Bearer
200 OK
{
  "id": "uuid",
  "email": "[email protected]",
  "name": "Alex Lee",
  "role": "agent",
  "status": "available",
  "channels": ["chats", "tickets"],
  "groups": [{ "id": "uuid-billing", "name": "Billing" }],
  "groupIds": ["uuid-billing"],
  "avatarUrl": "https://...",
  "hasPassword": true,
  "inviteStatus": "joined",
  "createdAt": "2026-01-04T12:00:00Z"
}

Invite an agent

POST/api/v1/agents/inviteAuth: Bearer

There is no direct-create endpoint. Agents always join by email invite: this sends an invitation email containing a one-time /join/{code} link the recipient uses to set their password. Pending invites count against the seat cap. Admin-only.

FieldTypeDescription
emailrequiredstringInvitee email. Must be unique across the workspace.
namerequiredstringDisplay name. Pre-fills the invitee&apos;s profile.
rolerequiredenumOne of admin, supervisor, agent.
channelsoptionalstring[]Subset of chats, tickets, calls. Omit to inherit the default (admins get all three, others get chats). Admins always see all channels regardless of this field.
groupIdsoptionaluuid[]Initial department memberships.
Body
{
  "email": "[email protected]",
  "name": "Alex Lee",
  "role": "agent",
  "channels": ["chats", "tickets"],
  "groupIds": ["uuid-billing"]
}

Update an agent

PATCH/api/v1/agents/:idAuth: Bearer

Change role, channels, department memberships (groupIds or departments, both accepted), or display name. Admins can edit anyone; supervisors can edit only their own department members; agents can edit only themselves (and only name + avatar).

Set status

POST/api/v1/me/statusAuth: Bearer

Sets the calling agent's availability. Drives routing (only available agents receive new assignments) and presence indicators in the inbox.

Body
{ "status": "away" }

Allowed values: available, away, offline.

Deactivate an agent

DELETE/api/v1/agents/:idAuth: Bearer

There is no dedicated /suspend action. DELETE is the suspend: it soft-deactivates the agent (status: "offline", role: "agent", password cleared, JWT invalidated) rather than deleting the row. Their assigned conversations fall back to their department or the workspace queue. Admin-only, and you cannot deactivate yourself or the workspace owner (the oldest admin).

Reinstate a deactivated agent

POST/api/v1/agents/:id/resend-inviteAuth: Bearer

There is no direct reinstate action either. A deactivated agent's password was cleared, so they look unjoined again: resending their invite and having them redeem it (the same /join/{code} flow as a first-time invite) reactivates the account.

Errors on delete

You cannot deactivate the last admin, yourself, or the workspace owner. The API returns 400 cannot_deactivate_self or 400 cannot_deactivate_owner.

Errors

CodeWhen
400 invalid_bodyPayload validation failed.
400 last_adminPATCH tried to demote the last remaining admin.
400 cannot_demote_ownerPATCH tried to change the workspace owner's (oldest admin's) role.
400 cannot_deactivate_selfDELETE tried to deactivate the calling agent.
400 cannot_deactivate_ownerDELETE tried to deactivate the workspace owner.
403 admin_requiredCaller is not an admin (invite, update, resend-invite, and delete are all admin-only).
404 not_foundAgent does not exist in this workspace.
409 email_already_existsInvite or update used an email another agent in this workspace already has.
409 already_joinedResend-invite was called on an agent who already set a password.

Common pitfalls

  • Setting channels for admins. Ignored. Admins always see all channels.
  • Looking for a create-agent endpoint. There isn't one. Every agent starts as an invite (POST /api/v1/agents/invite); they exist in a pending state until they set a password.
  • Looking for a password field on invite. Passwords are set by the invitee via the invite link, not passed by the inviting admin.