Workspace
The organisation: people and access, settings, company and brands.
Privacy and analytics
We use necessary technical cookies and, only with your consent, analytics and marketing pixels to improve the website and measure campaigns.
The organisation: people and access, settings, company and brands.
Sets the agent write policy every brand of the workspace follows unless it has its own: read_only, approval or direct. null goes back to the default (approval).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.agent_policy.set |
| MCP tool | workspace_agent_policy_set |
| Scope | workspace.write |
| Minimum role | workspace.settings.edit |
| Human confirmation | No |
| Doors | Screens, Apps |
| Status | Available |
Input
{
policy: "read_only" | "approval" | "direct" | null
}Output
{
ok: true
}Creates a workspace service account: an identity for integrations with one brand role (admin, member, viewer) on every brand or on chosen ones. Needs a recent sign-in on the screen. Its tokens are created with workspace.service_tokens.create.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.service_accounts.create |
| MCP tool | workspace_service_accounts_create |
| Scope | workspace.write |
| Minimum role | workspace.api_keys.manage |
| Human confirmation | No |
| Doors | Screens |
| Status | Available |
Input
{
name: string // ≤ 80
description?: string | null
scope: "all_brands" | "selected_brands"
role: "admin" | "member" | "viewer"
brandIds?: string[] // ≤ 200 items · default []
}Output
{
id: string // uuid
}Disables a workspace service account and every token it holds. Needs a recent sign-in on the screen.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.service_accounts.disable |
| MCP tool | workspace_service_accounts_disable |
| Scope | workspace.write |
| Minimum role | workspace.api_keys.manage |
| Human confirmation | Yes |
| Doors | Screens |
| Status | Available |
Input
{
serviceAccountId: string // uuid
}Output
{
ok: true
}Issues a token (nmw_) for one of the workspace's service accounts with scopes, expiry and optionally some of its brands. Needs a recent sign-in on the screen. Returned once.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.service_tokens.create |
| MCP tool | workspace_service_tokens_create |
| Scope | workspace.write |
| Minimum role | workspace.api_keys.manage |
| Human confirmation | No |
| Doors | Screens |
| Status | Available |
Input
{
serviceAccountId: string // uuid
name: string // ≤ 80
expiresAt?: string | null // ISO date; empty takes the default lifetime.
scopes: string[] // ≤ 200 items
brandIds?: string[] // ≤ 200 items · default []
}Output
{
token: string
credential: {
id: string // uuid
kind: string
name: string
prefix: string
scopes: string[]
brandIds: string[] | null
createdAt: string
expiresAt: string
lastUsedAt: string | null
revokedAt: string | null
revokedSource: string | null
legacy: boolean
serviceAccountId: string | null
}
}Issues a replacement for a service token of the workspace; the old one keeps working for the grace hours given. Needs a recent sign-in on the screen.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.service_tokens.rotate |
| MCP tool | workspace_service_tokens_rotate |
| Scope | workspace.write |
| Minimum role | workspace.api_keys.manage |
| Human confirmation | No |
| Doors | Screens |
| Status | Available |
Input
{
id: string // uuid
graceHours: integer // 0–720
}Output
{
token: string
credential: {
id: string // uuid
kind: string
name: string
prefix: string
scopes: string[]
brandIds: string[] | null
createdAt: string
expiresAt: string
lastUsedAt: string | null
revokedAt: string | null
revokedSource: string | null
legacy: boolean
serviceAccountId: string | null
}
}Ends a service token of the workspace now. Needs a recent sign-in on the screen.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.service_tokens.revoke |
| MCP tool | workspace_service_tokens_revoke |
| Scope | workspace.write |
| Minimum role | workspace.api_keys.manage |
| Human confirmation | Yes |
| Doors | Screens |
| Status | Available |
Input
{
id: string // uuid
}Output
{
ok: true
}Submits a request for an agency or group workspace (name, contact, website, expected brands, intended use). Nömad reviews it before the workspace exists; returns the request id.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.requests.submit |
| MCP tool | workspace_requests_submit |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
name: string // ≤ 120
workspaceType: "agency" | "group"
companyWebsite: string | null
contactEmail: string // email · ≤ 180
expectedBrands: integer // 1–10000
intendedUse: string // ≤ 2000
}Output
{
requestId: string
}Renames a workspace. Only its owner, or Nömad staff who manage workspaces, may: the database decides and answers forbidden (workspace_owner_required) otherwise.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.rename |
| MCP tool | workspace_rename |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
name: string // ≤ 120
}Output
{
ok: true
}Saves the workspace's paying company (legal name, tax id, billing email, phone and address) and mirrors it into every open brand billed with the workspace; returns how many were synced. Owner or Nömad staff only (the database decides).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.company.save |
| MCP tool | workspace_company_save |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
company: {
legalName: string // ≤ 200
taxId: string // ≤ 32
billingEmail: string // ≤ 180
billingPhone: string // ≤ 40
address: Record<string, string> // line1, line2, city, state, postal_code and the ISO country code.
}
}Output
{
brandAccountsSynced: integer // -9007199254740991–9007199254740991
}Sets whether one brand is billed with its workspace's company (workspace, mirrored from then on) or with its own (own, with the company). Owner of the workspace or Nömad staff only (the database decides).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.brand_billing.set |
| MCP tool | workspace_brand_billing_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
brandId: string // uuid
source: "workspace" | "own"
company: {
legalName: string // ≤ 200
taxId: string // ≤ 32
billingEmail: string // ≤ 180
billingPhone: string // ≤ 40
address: Record<string, string> // line1, line2, city, state, postal_code and the ISO country code.
}
| null
}Output
{
source: "workspace" | "own"
}Sets one member's role in each brand of the workspace (brand id → admin, billing, member or viewer). Owner or Nömad staff only (the database decides). The person hears what changed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.members.brand_roles.set |
| MCP tool | workspace_members_brand_roles_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
userId: string // uuid
roles: Record<string, "admin" | "billing" | "member" | "viewer">
}Output
{
ok: true
}Gives one member every brand of the workspace, present and future, with one role. Owner or Nömad staff only (the database decides). The person hears what changed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.members.all_brands.set |
| MCP tool | workspace_members_all_brands_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
userId: string // uuid
role: "admin" | "billing" | "member" | "viewer"
}Output
{
ok: true
}Invites one email address to every brand of the workspace with one role and sends the invitation email. Owner only (the database decides who anchors it and that the person is not in the workspace yet).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.all_brands.send |
| MCP tool | workspace_invites_all_brands_send |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
email: string // email · ≤ 320
role: "admin" | "billing" | "member" | "viewer"
anchorBrandId?: string | null
}Output
{
ok: true
}Proposes moving a brand to another workspace: who carries over with which role, when the subscription changes hands and the day billing moves. The target workspace's owner accepts or declines with workspace.brand_transfers.decide.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.brand_transfers.request |
| MCP tool | workspace_brand_transfers_request |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
brandId: string // uuid
sourceWorkspaceId: string // uuid
targetWorkspaceId: string // uuid
carryMembers: Record<string, "admin" | "billing" | "member" | "viewer">
subscriptionHandover: "next_renewal" | "immediate"
billingEffectiveOn: string
note: string | null
}Output
{
transferId: string
}Accepts, declines or cancels a pending brand transfer between workspaces. Returns what happens to the source workspace when it was its last brand.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.brand_transfers.decide |
| MCP tool | workspace_brand_transfers_decide |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
transferId: string // uuid
decision: "accept" | "decline" | "cancel"
carryMembers?: Record<string, "admin" | "billing" | "member" | "viewer"> | null
note?: string | null
workspaceIds: string[] // ≤ 2 items · The two workspaces of the transfer, to refresh their pages.
}Output
{
sourceWorkspaceOutcome: string | null
}Sets one person's role in the organisation (admin, member, billing, viewer). Ownership only moves by transfer. Needs workspace.members.manage (or Nömad staff who manage workspaces); nobody gives more than they hold (forbidden, detail grant_exceeds_actor:<keys>). changed is false when it already was that role.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.people.org_role.set |
| MCP tool | workspace_people_org_role_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
role: "admin" | "member" | "billing" | "viewer"
reason?: string // ≤ 500
userId: string // uuid
}Output
{
changed: boolean
}The same organisation role for up to 100 people, each through the same check and audit row as one person, so a refusal leaves the others done. Returns which were done and why the rest failed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.people.org_role.set_many |
| MCP tool | workspace_people_org_role_set_many |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
role: "admin" | "member" | "billing" | "viewer"
reason?: string // ≤ 500
userIds: string[] // ≤ 100 items
}Output
{
done: string[]
failed: Array<{
id: string
error: string
missing?: string[]
}>
}Sets one person's brand reach: every brand with one role, chosen brands with a role each, or none. Needs workspace.members.manage (or Nömad staff who manage workspaces). The person hears what changed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.people.access.set |
| MCP tool | workspace_people_access_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
access: {
scope: "all" | "selected" | "none"
allRole: "admin" | "member" | "viewer" | "billing" | null
brandRoles: Record<string, "admin" | "member" | "viewer" | "billing">
}
reason?: string // ≤ 500
brandIds?: string[] // ≤ 500 items · Brands whose pages refresh afterwards.
userId: string // uuid
}Output
{
ok: true
}The same brand reach for up to 100 people, each through the same check as one person. Returns which were done and why the rest failed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.people.access.set_many |
| MCP tool | workspace_people_access_set_many |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
access: {
scope: "all" | "selected" | "none"
allRole: "admin" | "member" | "viewer" | "billing" | null
brandRoles: Record<string, "admin" | "member" | "viewer" | "billing">
}
reason?: string // ≤ 500
brandIds?: string[] // ≤ 500 items · Brands whose pages refresh afterwards.
userIds: string[] // ≤ 100 items
}Output
{
done: string[]
failed: Array<{
id: string
error: string
missing?: string[]
}>
}Invites up to 50 email addresses to the organisation and its brands: role in the organisation, every brand with one role or chosen brands, an optional note and link validity (1, 7 or 30 days). One link per person, emailed now. Returns who was sent and why the rest failed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.send |
| MCP tool | workspace_invites_send |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
emails: string[] // ≤ 50 items
orgRole: "admin" | "member" | "billing" | "viewer"
scope: "all" | "selected"
allRole: "admin" | "member" | "viewer" | "billing" | null
brandRoles: Record<string, "admin" | "member" | "viewer" | "billing">
message?: string // ≤ 500
validDays?: 1 | 7 | 30 // default 7
}Output
{
sent: string[]
failed: Array<{
email: string
error: string
missing?: string[]
}>
}Mints a new link for a pending invitation (the old one stops working), with the same validity counted from now, and emails it again. Returns the new expiry.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.resend |
| MCP tool | workspace_invites_resend |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
inviteId: string // uuid
}Output
{
expiresAt: string
}Resends up to 100 pending invitations, each with a new link and the email again. Returns which were done and why the rest failed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.resend_many |
| MCP tool | workspace_invites_resend_many |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
inviteIds: string[] // ≤ 100 items
}Output
{
done: string[]
failed: Array<{
id: string
error: string
missing?: string[]
}>
}Mints a new link for a pending invitation to share by another channel; the emailed one stops working. Returns the link and its expiry. Links are only stored hashed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.copy_link |
| MCP tool | workspace_invites_copy_link |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | Screens, Apps |
| Status | Available |
Input
{
inviteId: string // uuid
}Output
{
url: string
expiresAt: string
}Ends a pending invitation now so its link opens nothing; the history keeps it with the reason. Needs brand.team.manage on its brand (brand action invite.revoke) and the database's own check.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.revoke |
| MCP tool | workspace_invites_revoke |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
inviteId: string // uuid
reason?: string // ≤ 500
}Output
{
ok: true
}Revokes up to 100 pending invitations, each through the same check as one. Returns which were done and why the rest failed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.revoke_many |
| MCP tool | workspace_invites_revoke_many |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
inviteIds: string[] // ≤ 100 items
reason?: string // ≤ 500
}Output
{
done: string[]
failed: Array<{
id: string
error: string
missing?: string[]
}>
}Declines an invitation addressed to the signed-in person, by its link token: it ends for good, the history keeps it and the inviter is told.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.invites.decline |
| MCP tool | workspace_invites_decline |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | Screens, Apps |
| Status | Available |
Input
{
token: string // ≤ 512
}Output
{
ok: true
}Asks for access to a brand (short id) with an optional message and role (viewer or member). Asking again returns the pending request. Whoever decides is told.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.access_requests.create |
| MCP tool | workspace_access_requests_create |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
brand: string // ≤ 64
message?: string // ≤ 500
requestedRole?: "viewer" | "member" | null
}Output
{
requestId: string | null
}Withdraws the signed-in person's own pending access request.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.access_requests.cancel |
| MCP tool | workspace_access_requests_cancel |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
requestId: string // uuid
}Output
{
ok: true
}Approves an access request with a role and brand reach, or denies it with an optional note. People who manage the organisation's people or the brand's team decide. The requester reads the outcome where they asked.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.access_requests.decide |
| MCP tool | workspace_access_requests_decide |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
requestId: string // uuid
workspaceId: string // uuid
brandId: string | null
decision: "approve" | "deny"
orgRole?: "admin" | "member" | "billing" | "viewer" | null
access?: {
scope: "all" | "selected" | "none"
allRole: "admin" | "member" | "viewer" | "billing" | null
brandRoles: Record<string, "admin" | "member" | "viewer" | "billing">
}
note?: string // ≤ 500
}Output
{
status: "approved" | "denied"
}Lists the email domains people can join the organisation with (mode, role, brand reach) and the owner's domains that can still be added. People managers and Nömad staff only.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/workspace.join_domains.read |
| MCP tool | workspace_join_domains_read |
| Scope | workspace.read |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
}Output
{
can_manage: boolean
domains: Record<string, unknown>[]
available: string[]
}Adds or changes a join domain: people with a confirmed address there join directly or ask, with which organisation role and brand reach. The domain must be the owner's (the database checks).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.join_domains.set |
| MCP tool | workspace_join_domains_set |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
domain: string // ≤ 260
mode: "join" | "request"
orgRole: "member" | "viewer"
brandScope: "none" | "all"
brandRole: "member" | "viewer" | null
reason?: string // ≤ 500
}Output
{
domain: string
}Removes a join domain: nobody else joins with it; people who already joined stay.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.join_domains.remove |
| MCP tool | workspace_join_domains_remove |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | Yes |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
domain: string // ≤ 260
reason?: string // ≤ 500
}Output
{
ok: true
}Joins an organisation the signed-in person may join with their confirmed company email, with the domain's role, or opens a request to join when the domain asks for one. Returns joined, pending or member.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.join |
| MCP tool | workspace_join |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
message?: string // ≤ 500
}Output
{
status: "joined" | "pending" | "member"
}Hands one person's open tasks, approver seats and pending invitations to a successor, and asks who connects again each connection that would stop working (tokens never change hands). The first half of removing someone; the successor is told once.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.people.handover |
| MCP tool | workspace_people_handover |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
workspaceId: string // uuid
brandId?: string | null
from: string // uuid
choice: {
to: string // uuid
tasks: boolean
approvals: boolean
invites: boolean
reconnect?: Record<string, string>
}
reason?: string // ≤ 500
}Output
{
tasks_moved: number
tasks_left: number
approvals_moved: number
approvals_left: number
invites_moved: number
}Closes a «connect it again» request addressed to the signed-in person.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/workspace.reconnect_requests.dismiss |
| MCP tool | workspace_reconnect_requests_dismiss |
| Scope | workspace.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
requestId: string // uuid
}Output
{
ok: true
}Updated on January 1, 1970