Skip to main content
The Trellis API uses a two-step authentication flow:
  1. Exchange your API key for a JWT token.
  2. Use the JWT token to authenticate every subsequent request.
The JWT is valid for six hours; refresh by re-exchanging your API key when it expires.

Obtaining an API key

API keys are provisioned by Trellis. Contact your organization administrator or Trellis support to obtain one.
Keep your API key secure. Do not expose it in client-side code or commit it to version control.

Computing the user_key

Every call to /v1/authenticate must include a user_key alongside the email. The user_key proves the request was authorized by your backend rather than forged client-side — any party with your API key can claim an email, so we require an HMAC-derived value that only your backend can produce.
The output is exactly 43 ASCII characters from the set [A-Za-z0-9_-]. Base64url-encoded without padding (strip trailing =). HMAC is over the raw bytes of the normalized email (lowercased, whitespace-trimmed), UTF-8 encoded.
The user_key is a signature, not an identifier. Trellis recomputes HMAC(secret, email) server-side using the email from the request and compares it to the submitted user_key. A user_key is bound to one specific email — submitting one user’s email with another user’s user_key always returns 401 Authentication failed, regardless of whether both users are provisioned. The only way to authenticate as a given user is to present their specific (email, user_key) pair.
The shared HMAC secret must stay on your backend. Never embed it in mobile apps, single-page apps, or any client-side code. Authenticate the user with your own login flow first, then compute user_key server-side and hand the (email, user_key) pair to the client to forward to Trellis.
To obtain the shared HMAC secret for your organization, contact Trellis support. Rotating the secret invalidates every outstanding user_key, so we coordinate timing with you.
Use the same normalization on both sides. The HMAC is computed over email.strip().lower(). If your normalization differs from Trellis’s, the HMAC won’t verify and the request will return 401. Send the email in the JSON body in its normalized form too — Trellis re-normalizes on its side, but matching ahead of time avoids edge cases with mixed-case domains.

Authentication flow

Step 1: Exchange API key for JWT

To start a session, send your API key, the user’s email address, and the computed user_key to the authenticate endpoint.
Response:
Users must be pre-provisioned by Trellis before they can authenticate. Auto-creation on first login was removed. To add a new user, contact your Trellis administrator with the email address you want enabled — authenticating with an unknown or disabled email returns 401 Authentication failed.

Step 2: Use the JWT token

Include the JWT token in the Authorization header for all API requests:

Token expiration

JWT tokens expire 6 hours after they are issued. The expires_at field in the authentication response tells you exactly when. When a token expires, you’ll receive a 401 Unauthorized response. Simply re-authenticate to get a new token.
For long-running applications, implement token refresh logic that re-authenticates before the token expires.

Rate-limit identity

Chat submission limits and the WebSocket connection cap are keyed by the authenticated member_id, not by the JWT. Re-authenticating refreshes credentials but does not reset a throttle or free a socket slot. Respect Retry-After, close sockets cleanly, and share one member-level budget across that user’s tokens and clients. The authenticate endpoint has its own separate limit of 60 requests per minute per API key.

Error responses

After request-shape validation, credential, provisioning, and HMAC failures return a single generic 401 Authentication failed so the specific failure mode isn’t disclosed to attackers. Structurally invalid input returns 400 or 422 as shown below.
A 401 against a known-good email usually means a normalization mismatch when computing user_key. Verify you’re lowercasing and trimming the email before HMAC, and that you’re feeding raw bytes (not the base64 string itself) of the decoded shared secret to the HMAC function.