On this page

Every request to the Gurb API is authenticated with a JWT bearer token obtained by logging in. There is currently no API-key or personal-access-token system — see Machine access below.

{
  "email": "admin@example.com",
  "password": "your-password"
}

The response contains a signed JWT. Send it on every subsequent request:

Authorization: Bearer <token>

The token payload carries userId, email, role, tenantId, and tenantSchema — so the token itself identifies which tenant you are acting in.

Lifetime: 7 days by default (JWT_EXPIRES_IN).

Two endpoints exist:

EndpointAuth requiredUse
POST /api/auth/refreshBearer tokenRotate while still valid
POST /api/auth/refresh-tokennone — send the refresh token in the bodyRotate after the access token expired

Most endpoints are tenant-scoped. The API resolves which community you mean in this priority order:

  1. Custom domain — the request's hostname (highest priority)
  2. communityId in the URL — path, query, body, or the X-Community-Id header
  3. X-Tenant-Id header
  4. The JWT's tenant claim (lowest priority)

In practice, call tenant-scoped endpoints with the community in the path and send the tenant header as well:

Authorization: Bearer <token>
X-Tenant-Id: <tenantId>

Debugging tip. A 400 Tenant context required or 401 No authentication token provided does not reliably mean your credentials are wrong. Six routers are mounted at the API root, so a URL that matches no route falls into their middleware before Express can conclude "no such route" — a typo'd path answers 400/401 instead of 404. Confirm the route exists before you debug your token.

StatusCodeMeaning
401UNAUTHORIZEDMissing, malformed, or expired token
403FORBIDDENAuthenticated, but lacking the required permission
403FEATURE_NOT_AVAILABLEThe community's plan does not include this feature
400TENANT_CONTEXT_REQUIREDNo community could be resolved from the request

FEATURE_NOT_AVAILABLE includes a featureKey field naming the feature that is off.

A community admin cannot currently issue an API token. Verified against the codebase:

  • there is no ApiKey, ApiToken, or PersonalAccessToken model in the Prisma schema
  • no API-key issuing or verification code exists anywhere in src/
  • every authenticated route resolves identity from a login-issued JWT

So server-to-server integrations today have only one option: log in as a real user account and refresh the JWT before it expires. That works, but it carries the usual costs of password-based machine access — the integration holds a human's credentials, the token inherits that human's full permissions, it cannot be scoped, and revoking it means changing the password or disabling the account.

If first-class machine access is needed, it requires building:

  • a scoped, revocable credential (ApiKey) owned by a community rather than a person
  • a hashed-at-rest secret shown exactly once at creation
  • per-key permission scopes reusing the existing permission system
  • an admin UI plus an audit trail of issue/revoke events

That is a design decision, not a documentation gap — this page records the current state accurately rather than describing something that does not exist.