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, orunknown) for an email. The SSO branch returns an opaque continuation token you redeem atGET /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/requestemails a code;POST /api/v1/auth/otp/verifyexchanges 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 atPOST /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'skey_prefix(never the full secret), type, owner, and revocation state. - Update scope on a key:
PATCH /api/v1/auth/api-keys/{id}with a newoperation_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
- API Reference overview — base URL and the service prefix map.
- AI Employee API — build and publish AI Employees once you can authenticate.
- Triggering AI Employees — use a JWT or API key to start runs from your own systems.