message.comDevelopers

Workspaces Live

Two separate endpoints, not one: a narrow office-hours/state read at /me/workspace, and the full-but-basic settings record at /workspaces/:id (name, timezone, locale only). There is no /workspaces/me.

One business equals one workspace. Multi-brand and agency operators run several workspaces and switch in the dashboard. See Concept: workspace.

Office-hours state (sidebar indicator)

GET/api/v1/me/workspaceAuth: Bearer

Polled minute-by-minute by the dashboard sidebar. Returns a thin workspace summary plus officeHoursOpen and nextChange, derived from the primary site's business-hours config.

Response shape

200 OK
{
  "workspace": {
    "id": "uuid",
    "name": "Acme Support",
    "timezone": "America/New_York",
    "locale": "en",
    "subdomainSlug": "acme-support",
    "emailDomainStatus": "verified"
  },
  "workspaceState": {
    "officeHoursOpen": true,
    "nextChange": "2026-05-13T22:00:00-04:00",
    "notifications": null
  }
}

Code samples

cURL
curl 'https://app.message.com/api/v1/me/workspace' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/me/workspace', {
  headers: { Authorization: 'Bearer ' + token }
});
const { workspace, workspaceState } = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/me/workspace",
    headers={"Authorization": f"Bearer {token}"},
)
data = r.json()
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/me/workspace")
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
data = JSON.parse(res.body)
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$data = json_decode(file_get_contents("https://app.message.com/api/v1/me/workspace", false, $ctx), true);

Get workspace settings

GET/api/v1/workspaces/:idAuth: Bearer

The :id must be the caller's own workspace ID (from GET /api/v1/me); any other ID returns 404. Deliberately narrow: no channels, no AI flags, no branding.

200 OK
{
  "workspace": {
    "id": "uuid",
    "name": "Acme Support",
    "timezone": "America/New_York",
    "locale": "en",
    "emailDomainId": "resend-domain-id",
    "emailDomainStatus": "verified"
  }
}

Update workspace settings

PATCH/api/v1/workspaces/:idAuth: Bearer

Admin-only. Partial update, no version check.

Body

FieldTypeDescription
nameoptionalstringDisplay name, 1 to 120 characters.
timezoneoptionalstringIANA timezone name, 1 to 64 characters.
localeoptionalstringLocale code, 2 to 16 characters.
Body
{
  "name": "Acme Support",
  "timezone": "America/New_York"
}

There is no branding (logo, color), channels, business-hours, or AI-flags field on this endpoint, no expectedVersion, and no delete-workspace action anywhere in the public API. Those live on other resources: Sites for widget branding and business hours, per-feature routes for AI flags.

Audit log

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

Admin actions on the workspace: invites, role changes, settings updates, suppression edits. Admin-only. See Audit log API.

Errors

CodeWhen
400 invalid_bodyBody failed validation.
400 no_changesPATCH sent with no recognized fields.
403 admin_requiredPATCH attempted by a non-admin.
404 not_found:id doesn't match the caller's own workspace.

Common pitfalls

  • Looking for /workspaces/me. Doesn't exist. Use /me/workspace for the state read, or /workspaces/:id (with your own ID) for settings.
  • Expecting branding or AI flags on this resource. Not here. Widget branding and business hours live on Sites.
  • Looking for a delete-workspace endpoint. There isn't one in the public API.