Service tokens

Endpoint: /api/auth/service-tokens/

Issue a service token for an application to run automation within that application’s roles and the token’s access-control rules. Its key starts with alpst-. A service token cannot call the users API or the applications API.

Authentication and permissions

Token management and history require a person signed in to the console or CLI. Create also accepts the caller’s own personal API token with service_token:create. Other management actions refuse personal API tokens; all management actions refuse service tokens with 403 permission_denied.

The examples with Authorization: Bearer <access_token> use a signed-in session. These routes require that session except create, which also accepts your own personal API token with service_token:create using the token authorization format. No service token can call them. Follow the setup guide to create and approve a token. A service token created from the CLI always waits for approval in the web console. See API authentication.

ActionRequired permission
List, get and historyservice_token:read globally or on the application
Createservice_token:create globally or on the application
Updateservice_token:update, plus permission to create tokens for that application
Rotateservice_token:rotate, plus permission to create tokens for that application
Disableservice_token:disable, plus permission to create tokens for that application
Deleteservice_token:delete
Activate or deactivate break-glassSuperuser account, signed in

An admin can read, create, update, rotate, disable and delete tokens. The application’s creator receives the application:maintainer role for it: they can read, create and update its tokens, but need an additional grant to rotate, disable or delete them. The service_token:manager role also grants read, create and update, globally or for an application. Break-glass is Superuser-only, even for an admin.

Writes are refused with 403 workspace_suspended while the workspace is suspended, except for activating or deactivating break-glass.

List tokens

Request

http
GET /api/auth/service-tokens/tokens/

Only tokens for applications on which you have service_token:read appear.

Query parameters

ParameterTypeDescription
namestringFilter by name
enabledbooleanFilter by enabled state; false includes pending tokens
activation_statestringpending or active
applicationUUIDFilter by application
expiringbooleanLive tokens expiring within 14 days
stalebooleanLive tokens whose key was issued more than 180 days ago
searchstringSearch name and description
orderingstringadded_at, updated_at, last_used_at, expires_at or last_issued_at; prefix with - for descending order. Default: -updated_at
pageintegerPage number, starting at 1
page_sizeintegerResults per page; default 15, maximum 100

Use activation_state=active&enabled=false to find switched-off tokens, and activation_state=pending for tokens awaiting approval.

Response

200 OK, using integer pagination:

json
{
  "count": 1,
  "current": 1,
  "next": null,
  "previous": null,
  "last": 1,
  "results": [
    {
      "id": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
      "name": "ci-deploy",
      "activation_state": "active",
      "enabled": true,
      "scopes": ["command:create", "command:read"],
      "expires_at": "2027-01-01T00:00:00Z"
    }
  ]
}

The example shows selected token fields. The next section lists the full set.

Example

bash
curl "https://your-workspace.us1.alpacon.io/api/auth/service-tokens/tokens/?activation_state=pending" \
  -H "Authorization: Bearer <access_token>"

Token fields

FieldTypeDescription
idUUIDRead-only token ID
namestringRequired on create; at most 128 characters. Unique among enabled and pending tokens; a disabled token’s name is available
descriptionstringOptional; default ""
applicationUUID / objectRequired UUID on create; read-only on update. Responses contain {id, name, service_type, username}
enabledbooleanRead-only on create; changing it requires approval
activation_statestringRead-only: pending or active
scopesarray[string]Each scope has at most 64 characters and names a catalog scope or wildcard. Supply scopes or presets on create
presetsarray[string]Create-only, write-only preset keys; default []
expires_atdatetimeRequired future ISO 8601 time on create; changing it requires approval
rotation_policystring30d, 90d, 180d or 365d; default 90d. Sets the new lifetime when you rotate; does not rotate automatically
ip_whitelistarray[string]IPv4/IPv6 addresses or CIDRs, each at most 64 characters; [] means unrestricted
rate_limitstring"" uses the fixed default of 1000/hour. An override is N/period, with a positive integer N and second, minute, hour, day, s, m, h or d; at most 32 characters
allowed_time_start, allowed_time_endtime / nullUTC HH:MM[:SS]; set both or neither. Null means unrestricted. A start after the end crosses midnight; an end on a whole minute includes that minute
allowed_daysarray[string]mon, tue, wed, thu, fri, sat, sun, case-insensitive; [] permits every day
justificationstringWrite-only, at most 1,024 characters; required for medium/high-risk creation and sensitive changes to tokens with medium/high-risk resulting scopes. Whitespace alone is empty
last_used_at, usage_countdatetime / integerRead-only usage; null unless you are an admin or can create tokens for the application
last_issued_atdatetimeRead-only; creation or latest rotation time
break_glass_active, break_glass_expires_atboolean / datetimeRead-only break-glass state
added_at, updated_atdatetimeRead-only timestamps
permissionsobjectRead-only {update, delete, rotate, disable} permission hints
keystringReturned only on create, including pending creation, and rotation
scope_usagearray / nullDetail response only; null unless you are an admin or can create tokens for the application

Create a token

Request

http
POST /api/auth/service-tokens/tokens/

The scopes must fit the application’s global roles, including when a Superuser creates the token. Presets are merged with explicit scopes; unavailable scopes are removed from presets. The resulting set cannot be empty. Explicitly requesting an unmintable scope returns 400 service_token_scope_not_mintable.

Service tokens cannot receive ftp_session:create, session:create, session:invite, session:share, tunnel_session:create, tunnel_session:reconnect, userchannel:create or userchannel:update. Cluster scopes are also refused when the workspace does not offer Kubernetes access.

Request body

json
{
  "name": "ci-deploy",
  "application": "9d2e4b71-0c3a-4f5e-8b6d-1a2c3e4f5a6b",
  "scopes": ["command:create", "command:read"],
  "expires_at": "2027-01-01T00:00:00Z",
  "rotation_policy": "90d",
  "justification": "Run deployment checks from CI"
}

Response

The token and its key are created in both cases:

StatusWhenToken state
201 CreatedA signed-in admin creates a low-risk token from the web consoleactivation_state: "active", enabled: true
202 AcceptedEvery other creation, including CLI requests, personal API tokens, non-admin callers and medium/high-risk scopesactivation_state: "pending", enabled: false

Any wildcard is high risk. Command, upload, download and session scopes with an access-control requirement are medium risk. Other scopes are low risk. A token with command scopes always returns 202 and requires justification, including when a signed-in admin creates it in the web console.

A 202 returns the full token, including key, and adds status, risk_level, risk_details and approval_request_id. Selected fields:

json
{
  "id": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "key": "alpst-xxxxxxxxxxxxxxxxxx",
  "activation_state": "pending",
  "enabled": false,
  "status": "pending_approval",
  "risk_level": "medium",
  "risk_details": [],
  "approval_request_id": "5cf4106b-b6c5-4ae3-b33c-e90e7a872155"
}

risk_details contains entries shaped as {scope, reason, level}; the array above omits those entries for brevity. Save the key from this response: the approver does not receive it. A pending token authenticates nowhere until approval. The approval request expires after 7 days.

A Superuser or an approver assigned to the request approves it in the web console; CLI and token calls cannot approve. The requester may approve their own request unless an approval-policy rule forbids it. Approval rechecks the application’s roles and the expiry time. Rejection, cancellation or expiry invalidates the pending key.

After approval, GET /api/auth/service-tokens/tokens/{id}/ returns 200 with activation_state: "active" and enabled: true. Only then can you add ACLs: ACL writes require an enabled, approved service token.

Example

bash
curl -X POST "https://your-workspace.us1.alpacon.io/api/auth/service-tokens/tokens/" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-deploy","application":"9d2e4b71-0c3a-4f5e-8b6d-1a2c3e4f5a6b","scopes":["command:create","command:read"],"expires_at":"2027-01-01T00:00:00Z","justification":"Run deployment checks from CI"}'

Get a token

http
GET /api/auth/service-tokens/tokens/{id}/

id is the token UUID. Returns 200 with the token fields and scope_usage, but no key. Read activation_state and enabled to check whether a pending token has been activated.

Update a token

Request

http
PATCH /api/auth/service-tokens/tokens/{id}/
PUT /api/auth/service-tokens/tokens/{id}/

name and description changes apply immediately. A request touching any of these sensitive fields returns 202 and waits for approval, for every caller:

  • scopes, enabled, expires_at, rotation_policy
  • ip_whitelist, rate_limit
  • allowed_time_start, allowed_time_end, allowed_days

This includes PATCH {"enabled": false}. Use Disable a token to stop use immediately. There is no enable route: request PATCH {"enabled": true} and wait for approval. The application’s roles are checked again when re-enabling a token.

Only one modification may be pending per token. A token still awaiting its initial approval cannot be updated. application is read-only: create a new token to use a different application. presets are refused on update.

PUT also supplies defaults for ip_whitelist, rate_limit and allowed_days, so it returns 202. Prefer PATCH when changing selected fields.

Response

An immediate update returns 200 with the token and no key. A sensitive update returns:

json
{
  "status": "pending_approval",
  "risk_level": "medium",
  "risk_details": [],
  "approval_request_id": "5cf4106b-b6c5-4ae3-b33c-e90e7a872155"
}

This body contains no key. risk_details uses {scope, reason, level} entries, omitted above for brevity. Scope changes must fit the application’s roles both when requested and when approved.

Example

bash
curl -X PATCH "https://your-workspace.us1.alpacon.io/api/auth/service-tokens/tokens/3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31/" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"rate_limit":"2000/hour","justification":"Support deployment checks across more servers"}'

Rotate a key

http
POST /api/auth/service-tokens/tokens/{id}/rotate/

Send an optional reason string of at most 1,024 characters. Rotation needs no approval, but your own global roles must cover the token’s stored scopes. Pending and disabled tokens cannot be rotated.

Returns 200 with the token and its new key. The old key stops authenticating immediately, with no overlap. Expiry resets to now plus rotation_policy, not the old expiry plus that duration; this can make the lifetime shorter. Update the secret in the automation that uses it in the same step.

bash
curl -X POST "https://your-workspace.us1.alpacon.io/api/auth/service-tokens/tokens/3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31/rotate/" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Scheduled key rotation"}'

Disable a token

http
POST /api/auth/service-tokens/tokens/{id}/disable/

Send an optional reason string of at most 1,024 characters. Returns 204 No Content and stops the token immediately without approval. It also ends all active break-glass access for the token. Pending tokens cannot be disabled.

A token is also disabled if an application role or permission is removed and its stored scopes exceed the application’s remaining permissions.

Delete a token

http
DELETE /api/auth/service-tokens/tokens/{id}/

Send an optional reason string of at most 1,024 characters. Returns 204 No Content; the token stops authenticating. Deleting a pending token also cancels its issuance request and invalidates its key. Its history remains available while the application exists.

Break-glass

Break-glass temporarily lifts only the IP whitelist, allowed hours and allowed days. The token must still be approved and enabled, with a valid, unexpired key. It cannot authenticate if its application has been deleted. Rate limits, scopes, application roles and ACLs still apply. See Access-control lists.

Activate

http
POST /api/auth/service-tokens/tokens/{id}/break-glass/

Only a signed-in Superuser can activate break-glass, including while the workspace is suspended.

FieldTypeRequiredDescription
justificationstringYesNonblank reason, at most 2,048 characters
duration_hoursintegerNo1–8 hours; default 4
json
{
  "justification": "Restore deployment access during the incident",
  "duration_hours": 2
}

Returns 201 with {id, service_token, activated_by, justification, activated_at, expires_at, deactivated_at, is_active}. A pending token is refused. Activating break-glass does not make a disabled or expired token usable. The exception remains effective only while the activating user is active and a Superuser; deactivation or demotion ends it.

Deactivate

http
DELETE /api/auth/service-tokens/tokens/{id}/break-glass/

Only a signed-in Superuser can deactivate it. Returns 204 and ends all active break-glass access for this token. If none is active, returns 400 service_token_break_glass_not_active. Activation and deactivation are recorded in history and send webhooks.

Token history

Request

http
GET /api/auth/service-tokens/history/?service_token={token_id}
GET /api/auth/service-tokens/history/{history_id}/

Both routes require service_token:read on the application. The list also requires the service_token query parameter: a missing parameter returns 400 service_token_history_scope_required, and an invalid or inaccessible token ID returns 404 api_not_found. Detail uses the history entry UUID without requiring that query parameter.

Query parameters

ParameterTypeDescription
service_tokenUUIDRequired token ID for the list
change_typestringOne change type
change_type__instringComma-separated change types
changed_byUUIDUser who made the change
added_at__gte, added_at__ltedatetimeInclusive timestamp bounds
orderingstringadded_at or -added_at; default -added_at
page, page_sizeintegerList pagination; default 15 results, maximum 100

Change types are created, activated, updated, rotated, disabled, deleted, break_glass_activated and break_glass_deactivated.

Response

The list returns 200 with count, current, next, previous, last and results. Detail returns one history entry:

json
{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "service_token_id": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "service_token_name": "ci-deploy",
  "change_type": "updated",
  "changed_by": null,
  "changes": {"description": {"old": "", "new": "Deployment checks"}},
  "reason": "Clarify the token's purpose",
  "added_at": "2026-10-24T10:00:00Z"
}

Keys are masked as ***. History remains readable after token deletion, but not after application deletion.

Read-only helpers

These routes help you choose scopes, presets and servers before creating a token or its ACLs. They require authentication. The scope and server helpers also require permission to create tokens for the requested application; a personal token reading the server helper needs server:read.

RequestParameters200 response
GET /api/auth/service-tokens/scopes/Required application UUID{resources: [{name, actions, acl, allowed_actions}], wildcards}. actions lists the workspace catalog; allowed_actions lists what the application can delegate. wildcards includes * only if the application holds it
GET /api/auth/service-tokens/presets/NoneObject keyed by preset ID; each value has localized name and description (en, ko) and a permissions array. Presets exclude unavailable extensions and scopes service tokens cannot receive
GET /api/auth/service-tokens/servers/Required application UUID; optional exclude_service_token UUID to omit servers the token already reaches, search by name, ordering (name or platform, default name), page, page_sizePaginated server objects {id, name, os, platform, is_connected, is_deleted} for servers both you and the application can reach. Default 15 results, maximum 100

Missing application returns 400 service_token_application_required. An invalid, unknown or deleted application returns 404 api_not_found; lacking permission to create tokens for it returns 403 service_token_application_forbidden.

Error responses

StatusError codeDescription
400unique, service_token_name_existsName conflicts with an enabled or pending token
400service_token_scopes_requiredNo scopes remain after preset expansion
400service_token_scope_invalid, service_token_invalid_presetInvalid scope or unknown/unavailable preset
400service_token_scope_not_mintableA requested scope cannot be issued to a service token
400service_token_scope_exceeds_roleScopes exceed the application’s roles
400service_token_scope_exceeds_rbacThe person rotating the token does not hold its scopes
400api_token_invalid_expirationExpiry is not in the future
400service_token_invalid_ip_whitelist, service_token_invalid_rate_limit, service_token_invalid_time_windowInvalid IP range, request rate, hours or days
400service_token_justification_requiredMissing justification for medium/high-risk issuance or a sensitive change
400service_token_pending_activationThe token is still awaiting its initial approval
400service_token_update_pendingA modification is already awaiting approval
400service_token_presets_not_allowed_on_updatePresets supplied on update
400service_token_disabledA disabled token cannot be rotated
400service_token_request_supersededAn issuance request can no longer activate its token
400required, blankRequired field missing or empty, including break-glass justification
400service_token_break_glass_not_activeNo active break-glass access to deactivate
400service_token_history_scope_required, service_token_application_requiredRequired history or helper parameter missing
401auth_token_missingAuthentication missing
403permission_deniedCredential type cannot call this management route
403service_token_application_forbiddenYou cannot create tokens for this application
403service_token_break_glass_unauthorizedBreak-glass caller is not a Superuser
403rbac_permission_required, rbac_object_permission_required, api_token_scope_missingMissing role permission or personal-token scope
403workspace_suspendedWrites refused while the workspace is suspended
404api_not_foundToken, history entry or application not found or not visible

When using the issued alpst- key, invalid, disabled, pending, expired or deleted tokens, including tokens on deleted applications, return 401 auth_authentication_failed. IP and time restrictions return 401 auth_ip_not_allowed or auth_access_outside_allowed_time. Exceeding the token’s rate limit returns 429 api_rate_limited with Retry-After; inability to verify a credential returns 503 auth_verification_unavailable. See API authentication.

Last updated: