Authentication

API keys, sessions, OAuth for MCP connectors, workspaces and roles.

Every request carries a bearer credential in the Authorization header. There are three kinds:

CredentialLooks likeUse it forWorkspace
API keyro_pat_...Scripts, integrations and MCP clientsImplied by the key
Session access tokena JWTThe web app (a signed-in person)X-Workspace-Id header
OAuth access tokenro_oat_...MCP connectors (Claude, ChatGPT, other MCP clients)Chosen at consent

API keys

Create keys in the app under Settings → API keys (only a signed-in person can create, list or revoke keys).

  • Format: ro_pat_ plus 43 characters. The key is shown once, at creation; only its SHA-256 hash and a short display prefix are stored. Keys are never logged.
  • Identity: a key belongs to one workspace and acts as the person who created it. Every request re-reads that person's membership, so a key is capped by their current role: if they are demoted or removed, their keys lose that access too.
  • Scopes: a key can only call routes its scopes allow. Granting admin needs the admin role.
  • Expiry: optional, from 1 to 3650 days (expires_in_days); without it a key doesn't expire.
  • Workspace: implied by the key. X-Workspace-Id is optional; if you send it, it must match the key's workspace (otherwise 403).
  • Audit: every mutating request made with a key (HTTP or MCP), and every key creation or revocation, is recorded in the workspace's audit log.
curl "$ZEROREACH_URL/api/v1/leads" -H "Authorization: Bearer $ZEROREACH_API_KEY"

Some routes never accept an API key, whatever its scopes: key management, the in-app assistant's routes, your own profile, password and sessions, whole-workspace privacy export and deletion, and approving or sending reply drafts (only a signed-in person does that). The API reference marks each route.

Errors

StatusWhen
401Invalid or expired API key. (with WWW-Authenticate: Bearer)
403The key lacks the route's scope, the route doesn't accept API keys, or X-Workspace-Id names another workspace
429Past the key's request rate (API_KEY_RATE_LIMIT_PER_MINUTE, default 600 per minute), or too many failed attempts from your IP

Sessions (the web app)

The web app signs people in with POST /api/v1/auth/login (email and password). The answer holds a short-lived access token (expires_in, 15 minutes by default) and sets an HttpOnly refresh cookie (30 days by default); POST /api/v1/auth/refresh rotates it. Reusing a rotated refresh token revokes the whole session family.

A session token names no workspace, so requests that act on one must send X-Workspace-Id: <workspace uuid>:

  • missing or not a UUID: 400;
  • a workspace you aren't a member of: 403 Not a member of this workspace. (the same answer whether or not it exists).

Signing out everywhere or changing the password revokes existing access tokens, and also the tokens of connected apps.

OAuth for MCP connectors

MCP clients that support the MCP authorization spec (for example Claude.ai custom connectors and ChatGPT connectors) can connect to /api/v1/mcp with OAuth instead of an API key:

  1. The client discovers the server from /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server (a 401 from the MCP endpoint names the metadata in WWW-Authenticate).
  2. It registers itself (POST /api/v1/oauth/register; public clients only; redirect URIs must be https:// or loopback http://).
  3. You sign in to ZeroReach, pick a workspace and approve on the consent screen. Authorization uses the code flow with PKCE (S256).
  4. The client gets an access token (ro_oat_..., 1 hour by default) and a rotating refresh token (ro_ort_..., 30 days by default).

Connector tokens:

  • carry the same scopes as API keys. Without a scope parameter a client gets every scope except admin;
  • are capped by your current role, like an API key;
  • are accepted only by the MCP endpoint.

Revoke a connection from the app, or with POST /api/v1/oauth/revoke. See Connect Zero via MCP.

Workspaces and roles

All customer data belongs to a workspace, and every query filters on it: another workspace's record behaves exactly like a missing one.

A person can belong to several workspaces, each with one role:

RoleCan
memberUse the workspace: leads, campaigns, inbox, calls, deals.
adminEverything a member can, plus workspace settings, members (admins and members), webhooks, discovery runs, signal rules, the audit log and other admin routes.
ownerEverything, including managing owners.

Rules for managing members:

  • members can't manage anyone;
  • admins manage admins and members;
  • only owners can grant the owner role, or change or remove an owner;
  • a workspace always keeps at least one owner.

Routes that need a higher role answer 403 Requires the <role> role or higher.