Service account API reference
Use the following REST API endpoints, field definitions, and error codes to manage service accounts and authenticate with the client credentials grant flow.
Service account endpoints
All service account management endpoints are under /api/v1/service_accounts. Project scoping is enforced through the project_id field in the request body (for creation) and visibility filters (for listing).
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/service_accounts |
Create a service account. |
GET |
/api/v1/service_accounts |
List service accounts. Supports pagination and filtering. Results are scoped to projects visible to the authenticated user. |
GET |
/api/v1/service_accounts/{service_account_id} |
Get details for a specific service account. |
PATCH |
/api/v1/service_accounts/{service_account_id} |
Update name or description. Theproject_id field is immutable after creation. |
DELETE |
/api/v1/service_accounts/{service_account_id} |
Delete a service account. Tokens issued to the account are rejected. |
POST |
/api/v1/service_accounts/{service_account_id}/enable |
Set the service account status toactive. |
POST |
/api/v1/service_accounts/{service_account_id}/disable |
Set the service account status todisabled. Tokens are rejected while disabled. |
Create request body
{
"name": "CI Pipeline",
"description": "Service account for Jenkins CI integration",
"project_id": "f9e8d7c6-b5a4-3210-fedc-ba0987654321"
}| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Human-readable name. Maximum 255 characters. |
description |
string |
No | Purpose or context for the service account. Maximum 2000 characters. |
project_id |
UUID |
Yes | UUID of the project that owns this service account. |
Labels are not accepted in the create request. To add labels to a service account, use the PATCH endpoint after creation.
Create response body
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "CI Pipeline",
"description": "Service account for Jenkins CI integration",
"status": "active",
"project_id": "f9e8d7c6-b5a4-3210-fedc-ba0987654321",
"last_authenticated_at": null,
"created_by": "12345678-abcd-efab-cdef-1234567890ab",
"created_at": "2026-07-02T14:30:00Z",
"updated_at": "2026-07-02T14:30:00Z",
"labels": {}
}The create response does not include credentials. To generate a client ID and client secret, create a credential as a separate step. For more information, see Create a service account.
Credential endpoints
Credentials are sub-resources of service accounts. Each service account can have up to 10 credentials.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/service_accounts/{sa_id}/credentials |
Create a credential. Returns the one-time plain text client secret (201). |
GET |
/api/v1/service_accounts/{sa_id}/credentials |
List credentials for a service account. Supports pagination. |
GET |
/api/v1/service_accounts/{sa_id}/credentials/{cred_id} |
Get details for a specific credential. |
DELETE |
/api/v1/service_accounts/{sa_id}/credentials/{cred_id} |
Delete a credential (204). |
POST |
/api/v1/service_accounts/{sa_id}/credentials/{cred_id}/rotate |
Rotate the secret. Returns the new plain text secret once. |
POST |
/api/v1/service_accounts/{sa_id}/credentials/{cred_id}/enable |
Set the credential status toactive. |
POST |
/api/v1/service_accounts/{sa_id}/credentials/{cred_id}/disable |
Disable the credential. |
Credential create request body
{
"credential_type": "client_credentials",
"expires_at": "2027-01-02T14:31:00Z"
}| Field | Type | Required | Description |
|---|---|---|---|
credential_type |
string |
Yes | Type of credential. Onlyclient_credentials is supported. |
expires_at |
datetime (ISO 8601 with timezone) |
No | Custom expiration timestamp. If omitted, auto-set from the configured maximum credential lifetime. Rejected if it exceeds the configured limit or is in the past. |
grace_period_seconds |
integer (0--86400) |
No | Duration in seconds that the old secret remains valid after rotation. Default:3600. |
Credential create response body
{
"id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"service_account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"credential_type": "client_credentials",
"identifier": "nx_sa_7kx9m2p4q1w3n5v8",
"client_secret": "generated-plaintext-secret",
"status": "active",
"grace_period_seconds": 3600,
"expires_at": "2026-12-29T14:31:00Z",
"old_secret_valid_until": null,
"last_used_at": null,
"created_by": "12345678-abcd-efab-cdef-1234567890ab",
"created_at": "2026-07-02T14:31:00Z"
}The client_secret field is included only in the credential create response and in the secret rotation response. It is not returned by any other endpoint. Record the secret immediately.
Grace period behavior
When you rotate a credential's secret, both the old and new secrets are accepted during a configurable grace period. This prevents downtime when multiple consumers need time to update their credentials.
The default grace period is 3600 seconds (1 hour). You can override the grace period per rotation by including grace_period_seconds in the rotate request body. This field accepts values from 0 to 86400 seconds (24 hours). Setting the grace period to 0 means the old secret is rejected immediately after rotation.
Token endpoint
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/auth/token |
Exchange client credentials for a JWT access token. |
Token request
The token endpoint accepts application/x-www-form-urlencoded requests with the following parameters:
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | Must beclient_credentials. |
client_id |
Yes | The credential'sidentifier value (client ID). Required when not using HTTP Basic authentication. |
client_secret |
Yes | The credential's client secret. Required when not using HTTP Basic authentication. |
Alternatively, you can provide the client_id and client_secret by using HTTP Basic authentication (RFC 6749 section 2.3.1). Encode the credentials as client_id:client_secret in Base64 and pass them in the Authorization header.
Token response
{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Im5leHVzLTEifQ...",
"token_type": "Bearer",
"expires_in": 900
}| Field | Type | Description |
|---|---|---|
access_token |
string |
ES256-signed JWT access token. |
token_type |
string |
AlwaysBearer. |
expires_in |
integer |
Token lifetime in seconds. The default is 900 seconds (15 minutes). The administrator can configure this value between 1 and 60 minutes. |
No refresh token is issued. When the access token expires, request a new one by repeating the client credentials grant flow.
ServiceAccount fields
| Field | Type | Description |
|---|---|---|
id |
UUID |
Automatically generated primary key. Also the principal ID in theprincipals table. |
name |
string (max 255) |
Human-readable name. |
description |
string (max 2000) |
Optional description. |
status |
string |
Operational status:active ordisabled. |
project_id |
UUID |
UUID of the owning project. |
last_authenticated_at |
datetime ornull |
Timestamp of the most recent successful authentication. |
created_by |
UUID |
UUID of the user who created the service account. |
updated_by |
UUID ornull |
UUID of the user who last modified the service account. |
created_at |
datetime |
Creation timestamp. |
updated_at |
datetime |
Last modification timestamp. |
labels |
object |
Key-value metadata. Both keys and values must be strings. |
ServiceAccountStatus values
| Value | Description |
|---|---|
active |
The service account can authenticate and access resources. |
disabled |
The service account cannot authenticate. Existing tokens are rejected. |
ServiceAccountCredential fields
| Field | Type | Description |
|---|---|---|
id |
UUID |
Automatically generated primary key. |
service_account_id |
UUID |
UUID of the parent service account. |
credential_type |
string |
Type of credential:client_credentials. |
identifier |
string (max 64, unique) |
Public client ID. Uses thenx_sa_ prefix, for examplenx_sa_7kx9m2p4q1w3n5v8. |
old_secret_valid_until |
datetime ornull |
When the previous secret stops being accepted. |
grace_period_seconds |
integer (0--86400) |
Duration in seconds that the old secret remains valid after rotation. Default:3600. |
status |
string |
Operational status:active ordisabled. |
expires_at |
datetime ornull |
Credential expiry timestamp. Auto-set from the configured maximum credential lifetime if not specified at creation. Reset to a new full lifetime on rotation. |
last_used_at |
datetime ornull |
Timestamp of last use. |
created_by |
UUID |
UUID of the user who created the credential. |
updated_by |
UUID ornull |
UUID of the user who last modified the credential. |
created_at |
datetime |
Creation timestamp. |
updated_at |
datetime |
Last modification timestamp. |
Access token JWT claims
Service account access tokens include the following claims:
| Claim | Type | Description |
|---|---|---|
sub |
string |
Service account UUID. |
iss |
string |
Automation orchestrator server URL (same issuer as human tokens). |
iat |
integer |
Issued-at timestamp (UNIX epoch seconds). |
exp |
integer |
Expiration timestamp (UNIX epoch seconds). |
token_type |
string |
Set toservice_account to distinguish from human user tokens. |
preferred_username |
string |
Service account name. |
aud |
string |
Token audience, for exampleorchestrator-api. |
token_ver |
integer |
Token version, used for per-account revocation. |
cred_id |
string ornull |
UUID of the credential used to obtain this token. Used for per-credential token revocation. |
groups |
array |
Always empty for service accounts (service accounts do not support group membership). |
Error responses
All error responses use the application/problem+json format (RFC 9457).
Token endpoint errors
| HTTP status | Error code | Condition |
|---|---|---|
401 |
AUTHENTICATION_REQUIRED |
Unknown client ID, incorrect secret, disabled account, or deleted account. The error is generic to prevent client ID enumeration. |
Management endpoint errors
| HTTP status | Error code | Condition |
|---|---|---|
400 |
Invalid request body (validation error). | |
400 |
CREDENTIAL_EXPIRATION_EXCEEDED |
The requestedexpires_at exceeds the configured maximum credential lifetime. |
400 |
CREDENTIAL_EXPIRATION_IN_PAST |
The requestedexpires_at is in the past. |
401 |
Missing or invalid authentication credentials. | |
403 |
Insufficient permissions for the requested action. | |
404 |
Service account or project not found. | |
409 |
SERVICE_ACCOUNT_NAME_CONFLICT |
A service account with the same name already exists in the project. |
409 |
SERVICE_ACCOUNT_CREDENTIAL_LIMIT |
The service account has reached the maximum of 10 credentials. |
422 |
Request body fails schema validation. |