MailLandDocs

Workspaces

Create and manage multiple workspaces for different projects or clients

Workspaces (tenants) are isolated environments for your email operations. Each workspace has its own domains, subscribers, API keys, and billing. A single user account can belong to multiple workspaces.

This page covers the tenants API at /v1/account/tenants: the "workspace" of this page is a tenant (billing/domain isolation boundary). /v1/account/workspaces is a different, separate feature: sub-workspaces used to organise resources within one tenant, and is not documented on this page.

Sub-workspaces organise, tenants separate

A sub-workspace (/v1/account/workspaces) only organises resources inside one tenant, using the existing roles. It is not a billing, deliverability or security boundary. These always belong to the tenant:

  • Billing: plan, subscription and invoices
  • The suppression list
  • Sending limits and quotas
  • Sending IPs and sender reputation
  • API key security

If you run an independent business or serve a separate customer, create a separate tenant for it, not a sub-workspace. Otherwise that customer shares the suppression list, sending limits and reputation of everyone else in the tenant.

Verification Levels

Each workspace has a verification level that determines sending capabilities:

LevelValueDescription
Restricted-1Sending disabled (violation detected)
Sandbox0Default for new workspaces: can only send to the workspace's sandbox allow-list
BasicVerified1Email, phone and 2FA all verified, moderate limits
SenderVerified2Domain authentication or a verified single sender, standard limits
BusinessVerified3Business verification complete: full limits

New workspaces start at Sandbox level, where sending is restricted to a per-workspace allow-list of recipients. BasicVerified requires email, phone and two-factor authentication together, not email alone. SenderVerified requires domain authentication (SPF+DKIM+DMARC) or a verified single sender: either one, not domain verification specifically.

List Workspaces

Get all workspaces the current user belongs to:

GET https://api.mail.land/v1/account/tenants
List workspaces
curl https://api.mail.land/v1/account/tenants \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Workspaces list response
[
  {
    "publicId": "k3m9x2p7qw4rtz",
    "name": "My Company",
    "role": "owner",
    "isActive": true,
    "status": "active"
  },
  {
    "publicId": "t2p9k4m7x1n6w3",
    "name": "Client Project",
    "role": "member",
    "isActive": true,
    "status": "active"
  }
]

The response is a bare array, not { "tenants": [...] }. There is no id, roleName, verificationLevel, verificationLevelName, isCurrent or createdAt field here, isActive and status replace isCurrent, and verification level is fetched separately (see Billing). publicId is the only identifier and is the value to use in API calls and UIs.

Create a Workspace

POST https://api.mail.land/v1/account/tenants
Create workspace request
{
  "name": "New Project",
  "description": "Optional workspace description"
}
FieldTypeRequiredDescription
namestringYesWorkspace name, 1–255 characters
descriptionstringNoOptional description, up to 500 characters
Create a workspace
curl -X POST https://api.mail.land/v1/account/tenants \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "New Project" }'

Response

Returns 201 Created with the same shape as Switch Workspace's response: new tokens scoped to the new workspace, plus the workspace record nested under tenant:

Workspace response
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "rt_new_token...",
  "expiresAt": "2026-02-17T13:00:00Z",
  "tenant": {
    "publicId": "u6w1m8k3p5x9n2",
    "name": "New Project",
    "role": "owner",
    "isActive": true,
    "status": "active"
  }
}

The creating user is automatically assigned the Owner role. There is no verificationLevel/verificationLevelName in this response, see Billing for verification/policy state.

Switch Workspace

Switch the active workspace for subsequent API calls:

POST https://api.mail.land/v1/account/tenants/switch
Switch workspace request
{
  "tenantId": "t2p9k4m7x1n6w3",
  "refreshToken": "rt_a1b2c3d4e5..."
}

refreshToken is optional: if omitted, the value from the refresh-token cookie is used instead. tenantId is the workspace's publicId from List Workspaces.

Switch workspace
curl -X POST https://api.mail.land/v1/account/tenants/switch \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{ "tenantId": "t2p9k4m7x1n6w3" }'
Switch workspace response
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "rt_new_token...",
  "expiresAt": "2026-09-25T15:00:00Z",
  "tenant": {
    "publicId": "t2p9k4m7x1n6w3",
    "name": "Client Project",
    "role": "member",
    "isActive": true,
    "status": "active"
  }
}

Switching workspaces issues new tokens scoped to the target workspace. The tenant fields are the same shape as the list workspaces response, nested under tenant rather than flattened as tenantId/tenantName/role.

Update Workspace

Update the current workspace settings:

PUT https://api.mail.land/v1/account/tenants/current
Update workspace request
{
  "name": "Updated Project Name",
  "description": "Optional workspace description"
}

name is 2–100 characters; description is optional, up to 500 characters.

Update workspace
curl -X PUT https://api.mail.land/v1/account/tenants/current \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Updated Project Name" }'

Only Owners and Admins can update the workspace. Members, Viewers and API keys get a typed 403 INSUFFICIENT_PERMISSIONS (with the missing permission, tenant.edit, in details.permission).

Response

Returns 200 OK:

Update workspace response
{
  "publicId": "t2p9k4m7x1n6w3",
  "name": "Updated Project Name",
  "description": "Optional workspace description",
  "status": "Active",
  "createdAt": "2026-01-15T08:00:00Z"
}

Workspace Context

All API requests are scoped to the active workspace. The workspace is determined by the token's active-workspace claim. Resources (domains, subscribers, campaigns, API keys) are isolated between workspaces.

When using API keys, each key is scoped to a single workspace. No workspace switching is needed: the key determines the context automatically.

Error Responses

StatusCodeDescription
400VALIDATION_FAILEDInvalid workspace name
403FORBIDDENNot a member of the target workspace
403INSUFFICIENT_PERMISSIONSUpdate only: caller is a Member, a Viewer or an API key, none of which hold tenant.edit
404TENANT_NOT_FOUNDWorkspace not found (update only: switching to an unknown or foreign workspace returns 403 FORBIDDEN, never 404)

Next Steps

On this page