Workspaces and access
A workspace holds your team, your plan and everything you send and receive: domains, inboxes, contacts and mail. API keys, members and roles decide who can do what.
The model
You sign in with your email, so one person can belong to several workspaces. A workspace has members, a
plan, and everything you read or send: domains, inboxes, contacts, templates, campaigns, webhooks and keys.
Every workspace also gets a private runearc.email domain to try things on (see Quickstart); inboxes
on it are ordinary inboxes of the workspace.
An API key belongs to one workspace and can be narrowed further (below). A signed in session picks the
workspace with the Runearc-Workspace header. GET /v1/me returns the signed in user, the workspace's plan,
usage and counts, and your role and permissions in it; GET /v1/me/workspaces lists the workspaces you belong to.
API keys
# A full access key
curl -X POST https://api.runearc.com/v1/keys \
-H "Authorization: Bearer $RUNEARC_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "production" }'
# A sending key limited to one domain, and a key limited to one inbox
curl -X POST https://api.runearc.com/v1/keys -H "Authorization: Bearer $RUNEARC_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "app", "permissions": "sending", "domain_id": "dom_..." }'
curl -X POST https://api.runearc.com/v1/keys -H "Authorization: Bearer $RUNEARC_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "support bot", "permissions": "inbox", "inbox_id": "ibx_..." }'| Field | Type | Description |
|---|---|---|
| name | string | Required, to recognise the key by. |
| permissions | string | full (default), sending or inbox. |
| domain_id | string | With sending: send only from that domain (id or name). |
| inbox_id | string | With inbox: only that inbox. An inbox key cannot send over SMTP or use analytics, campaigns or templates. |
The answer is 201 with key (id key_..., prefix, scope, permissions, last_used_at) and secret,
which starts with mb_live_ and is shown once; only a hash is stored. GET /v1/keys lists keys and
DELETE /v1/keys/{id} revokes one (204). Send it as Authorization: Bearer. Sending keys also work as the SMTP
password; see Mail apps and forwarding.
Domain setup
| Request | Body | Returns |
|---|---|---|
| POST /v1/setup | domain, mode, addresses | 201: the domain, its DNS records, inboxes, senders and a sending key. |
setup adds a domain and the addresses you want in one call. mode is root (receive on the domain),
subdomain (receive on mail.<domain>, leaving the root's mail alone) or sending_only; each address is
{ "local_part", "kind": "inbox" | "sender", "display_name" }. Publish the returned records, then verify; see
Domains.
Members and roles
A member has a workspace role: owner (exactly one), admin, member or billing. Owners and admins can do
everything, and billing sees billing and no mail. A member also has a member_role (admin, developer,
support or viewer, default viewer) and optionally a list of inbox_ids that narrows it to those inboxes;
no list means every inbox. Only owners and admins manage members.
curl -X POST https://api.runearc.com/v1/invitations \
-H "Authorization: Bearer $RUNEARC_API_KEY" -H "Content-Type: application/json" \
-d '{ "email": "sam@acme.com", "workspace_role": "member",
"member_role": "support", "inbox_ids": ["ibx_..."] }'Invitations expire after 7 days. The invitee signs in with the invited address and accepts with the link; accepting needs a signed in session, not an API key.
| Request | Body | Returns |
|---|---|---|
| GET /v1/members | none | Members: user, workspace_role, member_role, inbox_ids, joined_at, last_seen_at. |
| PATCH /v1/members/{user_id} | workspace_role, member_role, inbox_ids (all optional) | The member. inbox_ids replaces their inbox list; null or an empty list means every inbox. |
| DELETE /v1/members/{user_id} | none | 204. Also revokes their app passwords. |
| POST /v1/members/transfer-ownership | user_id | The new owner. Owner only. |
| GET, POST /v1/invitations | email, workspace_role, member_role, inbox_ids | Invitations; POST returns 201. |
| DELETE /v1/invitations/{id} | none | 204. Revokes it. |
| POST /v1/invitations/{id}/resend | none | The invitation, resent. |
| GET /v1/invitations/lookup?token= | none | What the invite page shows. No auth. |
| POST /v1/invitations/accept | token | Joins the workspace. Needs a session. |
Workspace settings, export and deletion
| Request | Body | Returns |
|---|---|---|
| PATCH /v1/workspace | name | The workspace. |
| GET, PATCH /v1/workspace/retention | retention_days (1 to 3650, or null to keep mail until deleted), audit_retention_days (30 to 3650, default 400) | The settings. |
| GET /v1/workspace/export | ?include_raw=true adds each message's .eml | A streamed NDJSON file, one {type, data} object per line. Owner only. |
| DELETE /v1/workspace | confirm: the workspace slug | Deletes the workspace and all its data. Owner only. |
| GET /v1/audit | action, actor_id, from, to, after, limit 1..100 | A page of audit events. |
| GET /v1/audit/export | format (csv or ndjson), from, to | The audit log as a file. |
GET /v1/account/export and DELETE /v1/account do the same for your own user account across workspaces. The
postal address for marketing mail is GET, PUT /v1/workspace/postal-address; see
Onboarding.
CLI and MCP
runearc login
runearc whoami
runearc keys create production
runearc keys create app-inbox --inbox ibx_...
runearc keys list
runearc keys revoke key_...Members, invitations and workspace settings have no CLI command or MCP tool; use the dashboard or the endpoints above with a session.
Errors
| Code | Status | Meaning and fix |
|---|---|---|
| insufficient_scope | 403 | This key's scope is too narrow. Use a full key. |
| invalid_role | 400 | Use the roles listed above. |
| already_member, already_invited | 409 | That person is already in, or invited to, the workspace. |
| invitation_not_found, invitation_invalid | 404 | The link is wrong, revoked or expired. Ask for a new invitation. |
| invitation_email_mismatch | 403 | Sign in with the address that was invited. |
| session_required, session_expired | 401 | Accepting needs a signed in session. |
| invalid_retention | 400 | Mail 1 to 3650 days or null; audit 30 to 3650. |
| confirmation_mismatch, confirmation_required | 400 | Send {"confirm": "<workspace slug>"}. |