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)
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
{
"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 'https://app.message.com/api/v1/me/workspace' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT'const res = await fetch('https://app.message.com/api/v1/me/workspace', {
headers: { Authorization: 'Bearer ' + token }
});
const { workspace, workspaceState } = await res.json();import requests
r = requests.get(
"https://app.message.com/api/v1/me/workspace",
headers={"Authorization": f"Bearer {token}"},
)
data = r.json()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
$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
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.
{
"workspace": {
"id": "uuid",
"name": "Acme Support",
"timezone": "America/New_York",
"locale": "en",
"emailDomainId": "resend-domain-id",
"emailDomainStatus": "verified"
}
}Update workspace settings
Admin-only. Partial update, no version check.
Body
| Field | Type | Description |
|---|---|---|
| nameoptional | string | Display name, 1 to 120 characters. |
| timezoneoptional | string | IANA timezone name, 1 to 64 characters. |
| localeoptional | string | Locale code, 2 to 16 characters. |
{
"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
Admin actions on the workspace: invites, role changes, settings updates, suppression edits. Admin-only. See Audit log API.
Errors
| Code | When |
|---|---|
400 invalid_body | Body failed validation. |
400 no_changes | PATCH sent with no recognized fields. |
403 admin_required | PATCH 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/workspacefor 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.