Skip to content

Authentication

WarmHub authenticates API requests using Bearer tokens passed in the Authorization header. For programmatic access, create a personal access token (PAT) using the CLI or the endpoints below.

TypeHow to obtainManages credentialsExpires
JWTInteractive login (CLI or web app)YesSession-based
PATPOST /api/pats (requires JWT)No30 days default, 1 year max

PAT management endpoints require JWT authentication — you cannot create, list, or revoke PATs using another PAT.

PATs carry scopes that limit their access:

ScopeGrants
repo:readRead repositories, queries, shapes
repo:writeCommits, shape mutations
repo:configureSubscriptions, credentials, repo settings
repo:adminDelete, archive, visibility
org:readRead org profiles
org:configureCreate repos, manage members
org:adminRename, archive org

Scopes are independent — each must be requested explicitly.

If no scopes are specified when creating a PAT, the token has no scope restrictions on standard endpoints. JWT tokens are not subject to PAT scope restrictions (they are still limited by the user’s org membership and role).

Pass the token in the Authorization header:

Terminal window
curl -H "Authorization: Bearer eyJhbGciOi..." \
https://api.warmhub.ai/api/repos/myorg/myrepo/head

Create a new personal access token. The token value is returned once — store it securely.

Auth: JWT only (no PAT).

FieldTypeRequiredDescription
namestringYesToken name (alphanumeric, hyphens, underscores)
scopesArray<{ resource?: string; permissions: string[]; allowedMatches?: string[] }>NoResource-scoped permission entries (see below)
structuredbooleanNoSet true when you need multiple entries for the same resource. Omit it for single-entry-per-resource payloads.
descriptionstringNoHuman-readable description
expiresAtnumberNoExpiration as epoch milliseconds. Default: 30 days. Max: 1 year.

Each scope entry binds a resource to a set of permissions:

FieldTypeRequiredDescription
resourcestringNoResource name: "org/repo" (repo-scoped), "org" (org-level), or omitted (global wildcard)
permissionsstring[]YesPermissions for this resource: "repo:read", "repo:write", etc.
allowedMatchesstring[]NoGlob patterns restricting which thing names this entry can access. Repo-scoped entries only — ignored (with warning) on org-level and global entries.

allowedMatches behavior:

  • Repo-scoped entries only. allowedMatches on org-level or global wildcard entries is stripped and a warning is returned in the response.
  • Patterns use glob syntax, for example Signal/* or Config/**.
  • When present, the token can only read or write thing names that match at least one pattern.
  • Omitting allowedMatches means no name restriction for that entry.
  • An empty array ([]) is valid and means deny-all for that entry.

The structured flag controls how duplicate entries are handled:

  • structured: true — multiple entries for the same resource are allowed, so you can assign different allowedMatches to different permissions. Each (resource, permission) pair must still be unique — duplicate pairs are rejected with a VALIDATION_ERROR.
  • structured omitted or false — duplicate resource entries are rejected (legacy behavior). Use this when each resource appears at most once.
  • allowedMatches without structuredallowedMatches does not require structured: true as long as the payload uses only one entry per resource.

Scope hierarchy (most specific wins): repo-scoped > org-level > global wildcard. Scopes can only narrow access, never escalate beyond the user’s role.

{
"token": "eyJhbGciOi...",
"name": "ci-deploy",
"scopes": [
{ "resource": "myorg/myrepo", "permissions": ["repo:read", "repo:write"] }
],
"expiresAt": 1743724800000,
"createdAt": 1741132800000
}

The response may include an optional warnings array (e.g., when allowedMatches was stripped from a non-repo entry).

CodeStatusDescription
ALREADY_EXISTS409A token with this name already exists for your account
VALIDATION_ERROR400Invalid name format or scope values
NOT_FOUND404A resource named in a scope entry does not exist
FORBIDDEN403A scope entry requests permissions that exceed your role on the target resource
UNAUTHENTICATED401Missing or invalid authentication
RATE_LIMITED429Too many PAT creation requests. Honor Retry-After before retrying.
Terminal window
# Single repo scope
curl -X POST https://api.warmhub.ai/api/pats \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-deploy",
"scopes": [
{ "resource": "myorg/myrepo", "permissions": ["repo:read", "repo:write"] }
],
"description": "CI/CD pipeline token"
}'
# Multiple scopes — repo-specific override + org-level fallback
curl -X POST https://api.warmhub.ai/api/pats \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "mixed-bot",
"scopes": [
{ "resource": "myorg/private-repo", "permissions": ["repo:read", "repo:write"] },
{ "resource": "myorg", "permissions": ["repo:read"] }
]
}'
# Name-restricted token with allowedMatches (set structured: true)
curl -X POST https://api.warmhub.ai/api/pats \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "signal-writer",
"structured": true,
"scopes": [
{ "resource": "myorg/myrepo", "permissions": ["repo:read"], "allowedMatches": ["Signal/*", "Config/*"] },
{ "resource": "myorg/myrepo", "permissions": ["repo:write"], "allowedMatches": ["Signal/*"] }
]
}'

List all personal access tokens for the authenticated user.

Auth: JWT only (no PAT).

[
{
"name": "ci-deploy",
"description": "CI/CD pipeline token",
"scopes": [
{ "resource": "myorg/myrepo", "permissions": ["repo:read", "repo:write"] }
],
"expiresAt": 1743724800000,
"createdAt": 1741132800000
}
]
FieldTypeDescription
namestringToken name
descriptionstringHuman-readable description (if set)
scopesArray<{ resource?: string; permissions: string[]; allowedMatches?: string[] }>Resource-scoped permission entries (if set)
expiresAtnumberExpiration timestamp (epoch ms)
revokedAtnumberRevocation timestamp (present only if revoked)
createdAtnumberCreation timestamp (epoch ms)
Terminal window
curl https://api.warmhub.ai/api/pats \
-H "Authorization: Bearer <jwt>"

Revoke a personal access token. Revoking an already-revoked token is a no-op (idempotent).

Auth: JWT only (no PAT).

ParameterTypeDescription
namestringToken name (URL-encoded)
{
"ok": true
}
CodeStatusDescription
NOT_FOUND404No token with this name exists for your account
UNAUTHENTICATED401Missing or invalid authentication
Terminal window
curl -X DELETE https://api.warmhub.ai/api/pats/ci-deploy \
-H "Authorization: Bearer <jwt>"