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:
| Level | Value | Description |
|---|---|---|
Restricted | -1 | Sending disabled (violation detected) |
Sandbox | 0 | Default for new workspaces: can only send to the workspace's sandbox allow-list |
BasicVerified | 1 | Email, phone and 2FA all verified, moderate limits |
SenderVerified | 2 | Domain authentication or a verified single sender, standard limits |
BusinessVerified | 3 | Business 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/tenantscurl https://api.mail.land/v1/account/tenants \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."[
{
"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{
"name": "New Project",
"description": "Optional workspace description"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Workspace name, 1–255 characters |
description | string | No | Optional description, up to 500 characters |
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:
{
"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{
"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.
curl -X POST https://api.mail.land/v1/account/tenants/switch \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{ "tenantId": "t2p9k4m7x1n6w3" }'{
"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{
"name": "Updated Project Name",
"description": "Optional workspace description"
}name is 2–100 characters; description is optional, up to 500 characters.
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:
{
"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
| Status | Code | Description |
|---|---|---|
400 | VALIDATION_FAILED | Invalid workspace name |
403 | FORBIDDEN | Not a member of the target workspace |
403 | INSUFFICIENT_PERMISSIONS | Update only: caller is a Member, a Viewer or an API key, none of which hold tenant.edit |
404 | TENANT_NOT_FOUND | Workspace not found (update only: switching to an unknown or foreign workspace returns 403 FORBIDDEN, never 404) |
Next Steps
- Team Management: invite members to your workspace
- Billing: manage workspace subscription
- Authentication: token and session management
- API Keys: create workspace-scoped API keys