API keys and access tokens
These endpoints manage the credentials of the API and what each credential can do: API keys, default capabilities, capability group rules, and refresh tokens with their short-lived access tokens. For how to send each credential, see Authentication.
API keys
An API key authenticates the CLI, Terraform, and API requests as a bearer token. The CLI calls API keys access tokens (stategraph user access-tokens).
Create API key
POST /api/v1/user/access-tokens
Request body: the required name labels the key. The optional capabilities object limits what the key can do, with the same model as whoami:
{
"name": "ci-prod-apply",
"capabilities": {
"commit": {
"states": { "a1b2c3d4-5678-90ab-cdef-1234567890ab": ["*"] }
}
}
}
Without capabilities, the key inherits all the session capabilities of its creator. Stategraph masks the requested capabilities with those of the creator, so a key never exceeds the identity that created it. See Access tokens and capabilities for the CLI equivalent and the full capability model.
Response: 201 Created. The response shows the key only one time. Store it securely.
{ "token": "eyJhbGciOiJIUzI1NiIs..." }
List API keys
GET /api/v1/user/access-tokens
Response:
{
"tokens": [
{
"id": "...",
"name": "my-api-key",
"created_at": "2026-06-04T10:30:00Z",
"owner_id": "...",
"owner_name": "jane@example.com",
"owner_type": "user"
}
]
}
Each token object always includes id, name, created_at, owner_id, owner_name, and owner_type. expiration is present only when the token has an expiry.
Revoke API key
POST /api/v1/user/access-tokens/revoke?token_id={id}
Revokes the key with the given ID.
Response:
{
"id": "f02791c8-aa63-4cdf-acae-a7c968d8a831",
"revoked": true
}
Capabilities
These endpoints manage the default capabilities that each session starts with, and the tenant group rules that grant more at login. The capabilities object has the same shape as in whoami. See Access tokens and capabilities for the CLI (stategraph caps).
Default capabilities
GET /api/v1/caps/default
PUT /api/v1/caps/default
Reads or replaces the default capabilities of the installation. Admin only (403 otherwise).
Request and response:
{
"capabilities": { "preview": {} }
}
List group rules
GET /api/v1/tenants/{tenant_id}/caps/group-rules
Lists the active capability group rules of the tenant. Requires admin of the tenant (an installation-wide admin qualifies). Each rule maps an identity-provider group condition to a capability grant. At login, the grant is added (a union) to the capabilities of each matching user.
Response:
{
"rules": [
{
"id": "...",
"condition": { "group": "eng-*" },
"grant": { "commit": { "tenants": ["550e8400-e29b-41d4-a716-446655440000"] } },
"description": "Engineers may apply",
"created_at": "2026-06-04T10:30:00Z",
"created_by": "f30ed1f9-be44-46a3-9050-03e9561e94f0"
}
]
}
Create group rule
POST /api/v1/tenants/{tenant_id}/caps/group-rules
Creates a rule that the tenant owns. The grant must be scoped to this tenant and no wider. The server rejects installation-level capabilities (unscoped admin, sudo, access-token create and refresh) and grants that name the states of another tenant: 400 when the grant is malformed, 422 when it reaches beyond this tenant.
Request body:
{
"condition": { "group": "eng-*" },
"grant": { "commit": { "tenants": ["550e8400-e29b-41d4-a716-446655440000"] } },
"description": "Engineers may apply"
}
Response: 200 OK with { "id": "..." }.
Delete group rule
DELETE /api/v1/tenants/{tenant_id}/caps/group-rules/{id}
Soft-deletes one of the rules of the tenant, and keeps the row for audit. The answer is 404 when this tenant has no such active rule.
Response: 200 OK
Refresh tokens and access tokens
Enterprise
The /api/v1/{vcs}/access-token endpoints are part of the Enterprise edition. They are available on Stategraph Cloud and on self-hosted Enterprise deployments. The Open Source edition does not have them. See Editions.
A refresh token is long-lived. It keeps the capabilities that you select when you create it, and it can only get access tokens. An access token is short-lived: it has the capabilities of its refresh token, and it expires after 60 seconds. For the full flow, see Access tokens.
Which token to send:
- For
POST /api/v1/access-token/refresh: the refresh token. This is the only endpoint that takes it. - For the
/api/v1/{vcs}/access-tokenendpoints: an access token from the refresh endpoint.
Authorization: Bearer YOUR_TOKEN
{vcs} is github or gitlab. The examples use github. For a GitLab installation, use gitlab in the path, for example /api/v1/gitlab/access-token.
Create refresh token
POST /api/v1/{vcs}/access-token
Creates a refresh token. Status codes: 200, 400, 403.
Request body (schema access-token-create):
{
"name": "string",
"capabilities": [
"kv_store_read",
"kv_store_write",
{
"name": "installation_id",
"id": "12345"
}
]
}
See Refresh token capabilities for all options.
Response (schema access-token):
{
"refresh_token": "string"
}
curl -X POST \
https://app.stategraph.cloud/api/v1/github/access-token \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Token",
"capabilities": ["kv_store_read", "kv_store_write"]
}'
List refresh tokens
GET /api/v1/{vcs}/access-token
Returns a page of refresh tokens. Status codes: 200, 403.
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | No | Maximum number of results |
page |
array[string] | No | Pagination token |
Response (schema access-token-page):
{
"results": [
{
"id": "string",
"name": "string",
"capabilities": [
"kv_store_read",
"kv_store_write",
{
"name": "installation_id",
"id": "12345"
}
]
}
]
}
curl -X GET \
"https://app.stategraph.cloud/api/v1/github/access-token?limit=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Delete refresh token
DELETE /api/v1/{vcs}/access-token
Deletes a refresh token by ID. Status codes: 200, 403, 404.
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | The ID of the token to delete |
curl -X DELETE \
"https://app.stategraph.cloud/api/v1/github/access-token?id=TOKEN_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Refresh access token
POST /api/v1/access-token/refresh
Exchanges a refresh token for an access token with the capabilities that you selected for the refresh token. Send the refresh token as the bearer token: YOUR_API_KEY in the example. Status codes: 200, 403.
Response schema:
{
"token": "string"
}
Example request:
curl -X POST \
https://app.stategraph.cloud/api/v1/access-token/refresh \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Refresh token capabilities
When you create a refresh token, you give it one or more capabilities. A capability is a string, or an object with more properties.
| Capability | Description |
|---|---|
access_token_refresh |
Refresh access tokens to get new short-lived tokens |
access_token_create |
Create refresh tokens |
kv_store_read |
Read the key-value store |
kv_store_write |
Write to the key-value store |
kv_store_system_read |
Read the system key-value store |
kv_store_system_write |
Write to the system key-value store |
{
"name": "My Token",
"capabilities": ["kv_store_read", "kv_store_write"]
}
Two object capabilities scope the token:
| Object | Properties | Scopes the token to |
|---|---|---|
| Installation ID | name: "installation_id". id: the installation ID string. |
One installation |
| VCS provider | name: "vcs". vcs: "github" or "gitlab". |
One VCS provider |
{
"name": "installation_id",
"id": "12345"
}
{
"name": "vcs",
"vcs": "github"
}
You can mix string and object capabilities:
{
"name": "Production Token",
"capabilities": [
"kv_store_read",
"kv_store_write",
{
"name": "installation_id",
"id": "67890"
},
{
"name": "vcs",
"vcs": "github"
}
]
}