message.comDevelopers

Audit log Live

The workspace audit log is the permanent record of administrative actions: agent invitations, role changes, department CRUD, conversation force-transfers, workspace settings updates, suppression edits, and subscription changes. Admin-only.

List audit entries

GET/api/v1/workspace/audit-logAuth: Bearer

Cursor-paginated, newest first. Only two query params: limit (1 to 200, default 50) and cursor (the opaque nextCursor from the previous page). There is no server-side filter by action, actor, or date range yet; filter client-side on the returned action and actorAgentId fields.

Code samples

cURL
curl 'https://app.message.com/api/v1/workspace/audit-log?limit=100' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/workspace/audit-log?limit=100', {
  headers: { Authorization: 'Bearer ' + token }
});
const { entries } = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/workspace/audit-log",
    headers={"Authorization": f"Bearer {token}"},
    params={"limit": 100},
)
audit = r.json()["entries"]
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/workspace/audit-log?limit=100")
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
audit = JSON.parse(res.body)["entries"]
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$audit = json_decode(file_get_contents("https://app.message.com/api/v1/workspace/audit-log?limit=100", false, $ctx), true)["entries"];
200 OK
{
  "entries": [
    {
      "id": "uuid",
      "actorAgentId": "uuid",
      "action": "agent.role_changed",
      "targetKind": "agent",
      "targetId": "uuid",
      "before": { "role": "agent" },
      "after": { "role": "supervisor" },
      "context": null,
      "createdAt": "2026-05-13T15:30:00Z"
    }
  ],
  "nextCursor": "2026-05-13T15:25:00Z|uuid"
}

Tracked actions

Action prefixExamples
agent.agent.invited, agent.role_changed, agent.deleted (deactivation), agent.reset_password
department.department.created, department.deleted, department.members_changed
workspace.workspace.updated, workspace.delete, workspace.domain_verified, workspace.sms_block
subscription.subscription.changed, subscription.trial_extended
conversation.conversation.force_transferred
suppression.suppression.removed
phone_number.phone_number.claimed, phone_number.purchased, phone_number.released, phone_number.updated
business_hours_rule.business_hours_rule.created, business_hours_rule.updated, business_hours_rule.deleted

Retention

  • Default: 365 days of audit entries.
  • Enterprise plans: unlimited retention, with export to your own SIEM.
  • Soft-delete: deleted records are tombstoned for 30 days before garbage collection.

The audit log is append-only at the API level. There is no edit or delete endpoint. If you need to redact a free-form field (e.g., a customer name in a transfer note), open a support ticket.

Not available yet: there is no export endpoint. Paginate the list endpoint with limit/cursor to build your own snapshot for SOC 2 / ISO 27001 audits.

Errors

CodeWhen
403 admin_requiredAgent role is not admin.
400 invalid_querylimit or cursor failed validation.

Common pitfalls

  • Treating audit as analytics. Use Reports for volume / performance. Audit is for compliance and security review.
  • Expecting server-side filters. There is no action, actorAgentId, or date-range filter. Page through and filter client-side.
  • Forgetting the actor. System-triggered events (auto-suspension, webhook-driven changes) carry actorAgentId: null.