Skip to content

API Tokens

API tokens give scripts, CI pipelines, and backend services programmatic access to your app with an explicit privilege list. Each token is a JWT signed with your app’s secret and carries exactly the privileges you grant it at creation, nothing more. The signed token string is returned once when the token is created and is never stored or shown again; only the metadata record (name, privileges, expiry, last use) remains readable afterwards.

Tokens come in two scopes:

  • App-scoped tokens act across your whole app, with no workspace restriction.
  • Workspace-scoped tokens are pinned to a single workspace (called a tenant in the API) and are what you hand to a customer’s integration.

To use an API token against the Bridge API, send it in the x-api-key header. To let your customers mint and manage their own tokens from inside your product, use the SDK-side guide at API tokens and the drop-in token management UI. You can also manage tokens visually in Control Center (the Bridge admin dashboard).

Treat a freshly created token like a password: capture the token field from the create response immediately and store it in your secrets manager. If it is lost, revoke the record and create a new token.

Three auth models appear on this page, and each endpoint below states which one it uses:

  1. App API key (/account/api-token/workspace* and /account/api-token/app*): authenticate with your app’s API key in the x-api-key header. Workspace-scoped endpoints additionally require the x-tenant-id header to pick the workspace.

    x-api-key: YOUR_APP_API_KEY
    x-tenant-id: TENANT_ID   (workspace endpoints only)
    
  2. User access token (/account/api-token/me/*): authenticate as a signed-in user with Authorization: Bearer <USER_ACCESS_TOKEN> (the token your app holds after login, see Authentication). The app is derived from the token’s aid claim, so no app header is needed. Creating and revoking requires the OWNER or ADMIN role.

  3. None (/account/api-token/introspect): intentionally unauthenticated; the API token being introspected is itself the credential.

All list and create endpoints return token records with the same shape:

| Field | Type | Description | |---|---|---| | id | string | The token record’s ID (used to revoke it) | | name | string | Human-readable label | | privileges | string[] | Privilege keys the token carries | | tenantId | string? | The workspace the token is pinned to; null for app-scoped tokens | | expireAt | string? | ISO-8601 expiry; null when the token never expires | | lastUsedAt | string? | ISO-8601 timestamp of the token’s last authenticated use; null if never used | | createdAt | string? | ISO-8601 creation timestamp |


Tokens pinned to a single workspace. These endpoints use app API key auth plus the x-tenant-id header.

Create a token restricted to one workspace. The workspace member identified by tenantUserId must hold the OWNER or ADMIN role in that workspace, otherwise the call returns HTTP 403. If your app configures an allow-list of token privileges (allowedTokenPrivileges), requesting a privilege outside that list also returns HTTP 403.

POST https://api.thebridge.dev/account/api-token/workspace

Headers

ParameterTypeRequiredDescription
x-api-keystringRequiredYour app API key
x-tenant-idstringRequiredID of the workspace the token is created in

Body Parameters

ParameterTypeRequiredDescription
namestringRequiredHuman-readable label for the token
privilegesstring[]RequiredPrivilege keys the token should carry (e.g. USER_READ)
expireAtstringOptionalISO-8601 expiry timestamp. Omit for a non-expiring token
tenantUserIdstringRequiredID of the workspace member (must be OWNER or ADMIN) creating this token

{ token, record }. token is the signed JWT, returned only this once; record is the token record.

Request example

curl --request POST 'https://api.thebridge.dev/account/api-token/workspace' \
--header 'x-api-key: YOUR_APP_API_KEY' \
--header 'x-tenant-id: TENANT_ID' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "CI deploy token",
  "privileges": ["USER_READ", "TENANT_READ"],
  "expireAt": "2027-01-01T00:00:00.000Z",
  "tenantUserId": "TENANT_USER_ID"
}'

Response example:

{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJhcHBJZCI6IjY0YTUuLi4ifQ.k2n8xJ...",
  "record": {
    "id": "665f1c2a9b3e4d5a6c7b8901",
    "name": "CI deploy token",
    "privileges": ["USER_READ", "TENANT_READ"],
    "tenantId": "650cd8510ccba777cc9623d0",
    "expireAt": "2027-01-01T00:00:00.000Z",
    "lastUsedAt": null,
    "createdAt": "2026-07-07T09:00:00.000Z"
  }
}
POST Try it out
POST https://api.thebridge.dev/account/api-token/workspace
Stored in session memory only. Never persisted.

List all token records for one workspace, newest first. Token strings are never included, only metadata.

GET https://api.thebridge.dev/account/api-token/workspace

Headers

ParameterTypeRequiredDescription
x-api-keystringRequiredYour app API key
x-tenant-idstringRequiredID of the workspace to list tokens for

Array of token records.

Request example

curl --request GET 'https://api.thebridge.dev/account/api-token/workspace' \
--header 'x-api-key: YOUR_APP_API_KEY' \
--header 'x-tenant-id: TENANT_ID'

Response example:

[
  {
    "id": "665f1c2a9b3e4d5a6c7b8901",
    "name": "CI deploy token",
    "privileges": ["USER_READ", "TENANT_READ"],
    "tenantId": "650cd8510ccba777cc9623d0",
    "expireAt": "2027-01-01T00:00:00.000Z",
    "lastUsedAt": "2026-07-06T18:22:41.000Z",
    "createdAt": "2026-07-07T09:00:00.000Z"
  }
]
GET Try it out
GET https://api.thebridge.dev/account/api-token/workspace
Stored in session memory only. Never persisted.

Delete a workspace token record. Revocation is instant: the backing record is checked on every authenticated call, so the JWT stops working immediately even if its own expiry has not passed. Returns HTTP 404 if the token does not exist or belongs to another app.

DELETE https://api.thebridge.dev/account/api-token/workspace/:TOKEN_ID

Headers

ParameterTypeRequiredDescription
x-api-keystringRequiredYour app API key
x-tenant-idstringRequiredID of the workspace the token belongs to

{ "success": true }

Request example

curl --request DELETE 'https://api.thebridge.dev/account/api-token/workspace/TOKEN_ID' \
--header 'x-api-key: YOUR_APP_API_KEY' \
--header 'x-tenant-id: TENANT_ID'

Response example:

{ "success": true }
DELETE Try it out
DELETE https://api.thebridge.dev/account/api-token/workspace/:TOKEN_ID
Stored in session memory only. Never persisted.
The ID of the token record

Tokens that act across the whole app, with no workspace restriction. These endpoints use app API key auth (no x-tenant-id).

List all token records for your app, newest first. This includes workspace-scoped records too (their tenantId is set); app-scoped records have tenantId: null.

GET https://api.thebridge.dev/account/api-token/app

Array of token records.

Request example

curl --request GET 'https://api.thebridge.dev/account/api-token/app' \
--header 'x-api-key: YOUR_APP_API_KEY'

Response example:

[
  {
    "id": "665f1d449b3e4d5a6c7b8944",
    "name": "Provisioning service",
    "privileges": ["USER_READ", "USER_WRITE", "TENANT_READ"],
    "tenantId": null,
    "expireAt": null,
    "lastUsedAt": "2026-07-07T06:14:02.000Z",
    "createdAt": "2026-06-01T12:00:00.000Z"
  }
]
GET Try it out
GET https://api.thebridge.dev/account/api-token/app
Stored in session memory only. Never persisted.

Create a token that is not restricted to any workspace. Use these for your own backend services and automation.

POST https://api.thebridge.dev/account/api-token/app

Body Parameters

ParameterTypeRequiredDescription
namestringRequiredHuman-readable label for the token
privilegesstring[]RequiredPrivilege keys the token should carry
expireAtstringOptionalISO-8601 expiry timestamp. Omit for a non-expiring token

{ token, record }. token is the signed JWT, returned only this once; record is the token record with tenantId: null.

Request example

curl --request POST 'https://api.thebridge.dev/account/api-token/app' \
--header 'x-api-key: YOUR_APP_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "Provisioning service",
  "privileges": ["USER_READ", "USER_WRITE", "TENANT_READ"]
}'

Response example:

{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJhcHBJZCI6IjY0YTUuLi4ifQ.p9w3aB...",
  "record": {
    "id": "665f1d449b3e4d5a6c7b8944",
    "name": "Provisioning service",
    "privileges": ["USER_READ", "USER_WRITE", "TENANT_READ"],
    "tenantId": null,
    "expireAt": null,
    "lastUsedAt": null,
    "createdAt": "2026-07-07T09:05:00.000Z"
  }
}
POST Try it out
POST https://api.thebridge.dev/account/api-token/app
Stored in session memory only. Never persisted.

Delete an app token record. Revocation is instant (see Revoke a workspace token). Returns HTTP 404 if the token does not exist or belongs to another app.

DELETE https://api.thebridge.dev/account/api-token/app/:TOKEN_ID

{ "success": true }

Request example

curl --request DELETE 'https://api.thebridge.dev/account/api-token/app/TOKEN_ID' \
--header 'x-api-key: YOUR_APP_API_KEY'

Response example:

{ "success": true }
DELETE Try it out
DELETE https://api.thebridge.dev/account/api-token/app/:TOKEN_ID
Stored in session memory only. Never persisted.
The ID of the token record

These endpoints power in-product token management (they are what the token management UI calls). They authenticate with the signed-in user’s access token, not the app API key:

Authorization: Bearer <USER_ACCESS_TOKEN>

The app is derived from the access token itself, so no app header is needed. Any authenticated user can list tokens and privileges; creating and revoking requires the OWNER or ADMIN role and returns HTTP 403 otherwise.

The privilege keys defined for the app, for building a privilege picker in your token-creation UI.

GET https://api.thebridge.dev/account/api-token/me/app/available-privileges

Array of { key, description } objects.

Request example

curl --request GET 'https://api.thebridge.dev/account/api-token/me/app/available-privileges' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN'

Response example:

[
  { "key": "USER_READ", "description": "Read users and roles" },
  { "key": "USER_WRITE", "description": "Create and update users" },
  { "key": "TENANT_READ", "description": "Read workspace data" }
]

List all token records for the signed-in user’s app, newest first.

GET https://api.thebridge.dev/account/api-token/me/app

Array of token records.

Request example

curl --request GET 'https://api.thebridge.dev/account/api-token/me/app' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN'

Response example:

[
  {
    "id": "665f1d449b3e4d5a6c7b8944",
    "name": "Provisioning service",
    "privileges": ["USER_READ", "USER_WRITE"],
    "tenantId": null,
    "expireAt": null,
    "lastUsedAt": "2026-07-07T06:14:02.000Z",
    "createdAt": "2026-06-01T12:00:00.000Z"
  }
]

Create an app-scoped token on behalf of the signed-in user. The user must hold the OWNER or ADMIN role.

POST https://api.thebridge.dev/account/api-token/me/app

Body Parameters

ParameterTypeRequiredDescription
namestringRequiredHuman-readable label for the token
privilegesstring[]RequiredPrivilege keys the token should carry (pick from available privileges)
expireAtstringOptionalISO-8601 expiry timestamp. Omit for a non-expiring token

{ token, record }. token is the signed JWT, returned only this once; record is the token record.

Request example

curl --request POST 'https://api.thebridge.dev/account/api-token/me/app' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "Reporting script",
  "privileges": ["USER_READ"],
  "expireAt": "2026-12-31T23:59:59.000Z"
}'

Response example:

{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJhcHBJZCI6IjY0YTUuLi4ifQ.h7t2mQ...",
  "record": {
    "id": "665f1f019b3e4d5a6c7b89aa",
    "name": "Reporting script",
    "privileges": ["USER_READ"],
    "tenantId": null,
    "expireAt": "2026-12-31T23:59:59.000Z",
    "lastUsedAt": null,
    "createdAt": "2026-07-07T09:10:00.000Z"
  }
}

Delete a token record on behalf of the signed-in user. The user must hold the OWNER or ADMIN role. Revocation is instant. Returns HTTP 404 if the token does not exist or belongs to another app.

DELETE https://api.thebridge.dev/account/api-token/me/app/:TOKEN_ID

{ "success": true }

Request example

curl --request DELETE 'https://api.thebridge.dev/account/api-token/me/app/TOKEN_ID' \
--header 'Authorization: Bearer USER_ACCESS_TOKEN'

Response example:

{ "success": true }

Check whether an API token is active and read its verified claims, in the style of RFC 7662. This is what backend Bridge plugins (for example bridge-nestjs) call when they receive an API token in x-api-key: instead of verifying the signature locally, they ask The Bridge, which re-checks the backing record on every call and therefore reflects revocation instantly.

This endpoint is intentionally unauthenticated: the presented token is itself the credential, and the response only echoes claims already inside that token. Any forged, invalid, expired, or revoked token yields { "active": false } and nothing else. Since only the issuing app’s secret verifies the signature, compare the returned appId against your own app ID before trusting the result.

POST https://api.thebridge.dev/account/api-token/introspect

Body Parameters

ParameterTypeRequiredDescription
tokenstringRequiredThe API token (JWT) to introspect

For an active token:

| Field | Type | Description | |---|---|---| | active | boolean | true when the token verifies and its record is live | | sub | string | The token record’s ID | | appId | string | The app the token was issued for. Compare against your own app ID | | tenantId | string? | The workspace the token is pinned to; null for app-scoped tokens | | type | string | Always api | | privileges | string[] | Privilege keys the token carries | | exp | number? | Unix expiry timestamp from the JWT; null for non-expiring tokens |

For anything else the body is exactly { "active": false }.

Request example

curl --request POST 'https://api.thebridge.dev/account/api-token/introspect' \
--header 'Content-Type: application/json' \
--data-raw '{ "token": "eyJhbGciOiJIUzI1NiJ9..." }'

Response example (active token):

{
  "active": true,
  "sub": "665f1c2a9b3e4d5a6c7b8901",
  "appId": "64a5f8e2b1c93a0012d45678",
  "tenantId": "650cd8510ccba777cc9623d0",
  "type": "api",
  "privileges": ["USER_READ", "TENANT_READ"],
  "exp": 1798761600
}

Response example (inactive token):

{ "active": false }
POST Try it out
POST https://api.thebridge.dev/account/api-token/introspect