Authentication

Every Ema API call must carry a credential that identifies the caller and fixes their tenant. The platform supports two: a short-lived Bearer JWT for user-driven access (what the builder UI uses), and a long-lived tenant API key for programmatic, machine-to-machine access. This page explains how to obtain each and how to send it.

Pick one per request. A request must carry exactly one credential. If the Authorization header is present, the platform validates it as a JWT and will not fall back to an API key. If there is no Authorization header, it validates the X-API-Key header. With neither, the call returns 401 Unauthorized.

JWT access tokens

A JWT (JSON Web Token) is a short-lived access token tied to a specific user, tenant, and role. It is the right credential for anything that acts on behalf of a signed-in person.

Sign in with email and password

POST /api/v1/auth/login exchanges credentials for tokens. This route is public — it needs no prior credential.

POST https://your-tenant.ema.co/api/v1/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "••••••••",
  "tenant": "acme"
}

The tenant field is optional. Omit it for email-first sign-in (the server routes you to your home tenant); include the tenant slug when signing in through a tenant-pinned URL.

A successful response returns the tokens and the resolved user:

{
  "access_token": "eyJhbGciOiJSUzI1Ni...",
  "refresh_token": "eyJhbGciOiJSUzI1Ni...",
  "user": {
    "id": "8c1f...",
    "tenant_id": "a4d2...",
    "email": "[email protected]",
    "name": "Avery Builder",
    "role": "builder",
    "status": "active"
  }
}

The builder UI uses cookies, not headers. When you sign in through the browser, the gateway stores access_token and refresh_token as HttpOnly cookies and promotes the access token to an Authorization: Bearer header on every backend call for you. The JSON body above is what programmatic clients use; the cookie flow is for the SPA. Either way the underlying token is the same.

Send the token

Put the access token in the Authorization header on every subsequent call:

GET https://your-tenant.ema.co/api/v1/workflow/workflows
Authorization: Bearer eyJhbGciOiJSUzI1Ni...

Refresh an expired token

Access tokens are short-lived. When one expires, exchange the refresh token for a new access token at POST /api/v1/auth/refresh:

POST https://your-tenant.ema.co/api/v1/auth/refresh
Content-Type: application/json

{ "refresh_token": "eyJhbGciOiJSUzI1Ni..." }
{ "access_token": "eyJhbGciOiJSUzI1Ni..." }

Refresh tokens are long-lived and are not rotated on refresh — keep reusing the one you received at sign-in until it expires. The endpoint returns only a new access token; there is no rolling-refresh scheme.

Other sign-in methods

Beyond email and password, the auth service supports:

  • SSO (SAML and OIDC). Sign-in begins at POST /api/v1/auth/begin, which returns the method (sso, password, or unknown) for an email. The SSO branch returns an opaque continuation token you redeem at GET /api/v1/auth/sso/continue, which redirects into the configured identity provider. The callback returns the same token shape as login.
  • One-time passcode (OTP). POST /api/v1/auth/otp/request emails a code; POST /api/v1/auth/otp/verify exchanges it for tokens (or a partial-auth token when TOTP is also required).
  • TOTP (two-factor). When a tenant requires it, OTP/SSO sign-in returns a partial_auth_token; complete the second factor at POST /api/v1/auth/totp/verify.

These are browser-driven flows; the email-and-password and refresh endpoints above are what most programmatic clients need.

Switch tenants

If your user belongs to more than one tenant, list them with GET /api/v1/auth/users/me/tenants and mint tokens for a different one with POST /api/v1/auth/switch-tenant:

POST https://your-tenant.ema.co/api/v1/auth/switch-tenant
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{ "target_tenant_id": "b7e9..." }

The response is a fresh LoginResponse (new tokens) scoped to the target tenant.

Tenant API keys

A tenant API key is a long-lived credential for programmatic access — scripts, backend services, and partner integrations that aren't tied to an interactive user session. It is the right credential for server-to-server automation.

Create a key

An administrator creates a key at POST /api/v1/auth/api-keys. This requires a JWT (you must be signed in to mint a key).

POST https://your-tenant.ema.co/api/v1/auth/api-keys
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{
  "name": "Nightly export job",
  "key_type": "user"
}

The response includes the full key once — store it securely, because it is never shown again:

{
  "id": "f0a1...",
  "name": "Nightly export job",
  "key": "ema_sk_live_2f9c...full-secret",
  "key_prefix": "ema_sk_live_2f9c",
  "key_type": "user",
  "created_at": "2026-06-01T12:00:00Z"
}

key_type is user (the default — a key owned by and acting as the creating user) or system (a tenant-level service key; creating one requires admin role). You may also pass operation_scope and resource_scope to narrow what the key can do — for example, restricting a key to a single AI Employee.

Send the key

Put the key in the X-API-Key header, with no Authorization header on the request:

GET https://your-tenant.ema.co/api/v1/workflow/workflows
X-API-Key: ema_sk_live_2f9c...full-secret

The key carries its own tenant and owner, so calls made with it behave as that owner within that tenant.

Manage keys

  • List the tenant's keys: GET /api/v1/auth/api-keys. The response shows each key's key_prefix (never the full secret), type, owner, and revocation state.
  • Update scope on a key: PATCH /api/v1/auth/api-keys/{id} with a new operation_scope / resource_scope.
  • Revoke a key: DELETE /api/v1/auth/api-keys/{id}. Revocation is immediate.

Treat keys like passwords. A tenant API key grants its owner's access to your tenant for as long as it lives. Never commit one to source control or embed it in client-side code. Rotate keys by creating a replacement and revoking the old one.

Roles and capabilities

Authenticating proves who you are; your role determines what you may do. The system roles, from most to least privileged, are env_admin, system_admin, builder_admin, user_admin, builder, user, and no_access. Building and publishing AI Employees generally requires builder, builder_admin, or system_admin. When a request is authenticated but the role lacks the required capability, the platform returns 403 Forbidden. When it lacks visibility of a resource, it returns 404 so existence is never leaked across tenants.

What's next

Last updated: Jul 3, 2026