Skip to content

REST API ​

Grant exposes a full REST API at /api/* with interactive Swagger documentation. All endpoints use JSON and follow consistent request/response patterns.

Swagger UI ​

The API ships with an interactive Swagger UI for exploring and testing every endpoint:

ResourceURL
Swagger UIhttp://localhost:4000/api-docs
OpenAPI JSONhttp://localhost:4000/api-docs.json

Swagger UI includes request/response schemas, example payloads, and a "Try it out" button for every endpoint. The OpenAPI spec can be imported into Postman, Insomnia, or used to generate client SDKs.

Authenticating in Swagger UI ​

Most endpoints require a JWT. Follow these steps to authenticate in the Swagger UI:

1. Get a token — Expand POST /api/auth/login under the Authentication tag, click "Try it out", and send:

json
{
  "provider": "email",
  "providerId": "admin@example.com",
  "providerData": { "password": "YourPassword1!" }
}

2. Copy the token — From the response, copy the accessToken value.

3. Authorize — Click the Authorize button (lock icon) at the top of the page, paste the token into the bearerAuth field (without the Bearer prefix), and click Authorize.

4. Test endpoints — All subsequent "Try it out" requests will include the token automatically.

Persist Authorization

Swagger UI is configured with persistAuthorization: true — your token survives page reloads until you click Logout in the Authorize dialog.

Base URL ​

http://localhost:4000/api

For production deployments, replace with your deployed API URL.

Authentication ​

Include a JWT access token in the Authorization header:

http
Authorization: Bearer <accessToken>

Obtain tokens via POST /api/auth/login (email/password) or POST /api/auth/register (new account). Refresh expired tokens with POST /api/auth/refresh.

Authentication Flow ​

bash
# 1. Login
curl -s -X POST http://localhost:4000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"provider":"email","providerId":"admin@example.com","providerData":{"password":"YourPassword1!"}}'

# Response: { "success": true, "data": { "accessToken": "eyJ...", "refreshToken": "eyJ...", "accounts": [...] } }

# 2. Use the token
curl -s http://localhost:4000/api/organizations \
  -H "Authorization: Bearer <accessToken>" \
  -H "Content-Type: application/json"

# 3. Refresh when expired
curl -s -X POST http://localhost:4000/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"accessToken":"<expired>","refreshToken":"<refreshToken>"}'

Endpoints by Tag ​

Every endpoint is fully documented in the Swagger UI. The table below links to each tag for quick navigation:

TagEndpointsDescription
Authentication10Login, register, refresh, logout, OAuth, token exchange, authorization check
Me17Profile, sessions, password, auth methods, notifications, preferences, data export
Organizations5CRUD for organizations
Organization Invitations7Invite, accept, resend, renew, revoke
Organization Members3List, update role, remove
Projects4CRUD for projects
Users5CRUD for users within a scope
Roles4CRUD for roles
Groups4CRUD for groups (permission bundles)
Permissions4CRUD for permissions
Resources4CRUD for resources (RBAC targets)
API Keys5Create, list, revoke, delete, token exchange
Webhook Subscriptions8Project webhook CRUD, secret rotate, deliveries, replay
Tags4CRUD for tags

REST-Only Endpoints

Some operations are only available via REST (not GraphQL):

  • GET /api/auth/github — Initiate GitHub OAuth (browser redirect)
  • GET /api/auth/github/callback — GitHub OAuth callback
  • GET /api/auth/project/app-info — Public project-app branding and scopes for hosted sign-in
  • GET /api/auth/project/consent-info — Public consent payload (resolved branding, scopes, signed-in user)
  • POST /api/auth/cli-callback — Exchange CLI one-time code for tokens
  • POST /api/auth/token — Exchange API key for JWT
  • POST /api/auth/is-authorized — Check authorization (used by SDKs)
  • GET /api/me/export — GDPR data export (file download)
  • GET /.well-known/jwks.json — JWKS public key discovery (3 scoped variants)

See Transport Layers for a full comparison.

Scoping and Multi-Tenancy ​

Most endpoints require a scope to specify the tenant context. The scope is passed differently depending on the HTTP method:

GET / DELETE — Query parameters:

bash
GET /api/roles?scopeId=<orgId>:<projectId>&tenant=organizationProject

POST / PATCH — Request body:

json
{
  "name": "Editor",
  "scope": { "tenant": "organizationProject", "id": "<orgId>:<projectId>" }
}

The tenant field indicates the level of the hierarchy. The id field is either a single UUID or two UUIDs joined by : (e.g., orgId:projectId).

TenantScope ID formatExample
account<accountId>Personal account scope
organization<orgId>Organization scope
accountProject<accountId>:<projectId>Personal project
organizationProject<orgId>:<projectId>Org project

A request is authorized in exactly one scope. For a session token that is the scope the request carries: the X-Scope-Tenant and X-Scope-Id headers when present, otherwise the scope in the query or body. For an API key or a project-app token it is the scope of the token itself. A scoped operation acts only in the scope it was authorized in: when its query or body names a scope (scope, scope inside input, or scopeId with tenant), tenant and id must equal the authorized scope, and the request is refused with 403 FORBIDDEN otherwise, before any lookup or permission check, so the response does not reveal whether the target exists. An operation that names no scope is unaffected. system is never accepted as a scope named in a request. The same rule applies to the scope argument and the scope field of the input argument in GraphQL, where the refusal is a Forbidden error.

TIP

See Multi-Tenancy for details on tenant isolation and the hierarchy model.

Response Format ​

Success:

json
{
  "success": true,
  "data": { ... }
}

Error:

json
{
  "error": "Localized error message",
  "code": "ERROR_CODE"
}

See Error Handling for the full error reference.

Field Selection ​

Load related entities on-demand with the relations query parameter:

bash
GET /api/roles?scopeId=...&tenant=organizationProject&relations=groups,tags

Only base fields are returned by default. See Field Selection for available relations per resource.

Rate Limiting ​

Rate limits are configurable via environment variables (SECURITY_RATE_LIMIT_*). Auth endpoints have stricter limits than general endpoints. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header. See Configuration for details.


Related:

Released under the MIT License.