Skip to main content
OpenAPI

Service Account APIs

Manage service accounts, Credits limits, long-lived keys, and short-lived tokens.

About this document

Service accounts let applications and automation use Qoder without creating an organization member or occupying a member seat. Enable the service account capability for your organization before using these APIs. Read Conventions and Get API Key first. Base URL: https://api.qoder.com. Replace all example IDs with real resource UUIDs from your organization; the example UUIDs do not identify existing resources.

Requirements

OperationBearer credential
Manage accounts and long-lived keysOrganization API key belonging to the organization in the path
Issue a short-lived tokenValid long-lived key belonging to the service account in the path
Use Authorization: Bearer <credential>. An organization API key or short-lived token cannot replace the service account's own long-lived key when issuing a token.

API list

1. Create service account

POST /v1/organizations/{organization_id}/service-accounts

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID

Request body

Create requires name.
{
  "name": "CI Automation",
  "description": "Production CI jobs",
  "limitAmount": 1000
}
ParameterTypeRequiredDescription
namestringYesTrimmed; 1–100 characters; unique within the organization
descriptionstringNoOptional, trimmed, up to 500 characters; an empty string clears it on update
limitAmountfloat64NoOptional: -1 means no independent limit; 0 freezes new Credits consumption; a finite positive value caps consumption. Defaults to -1 on create

Success response (201 Created)

{
  "id": "019c1234-5678-7abc-8def-012345678901",
  "organizationId": "019c1234-5678-7abc-8def-012345678900",
  "name": "CI Automation",
  "description": "Production CI jobs",
  "status": "active",
  "createdBy": "019c1234-5678-7abc-8def-012345678904",
  "createdAt": "2026-07-20T09:00:00Z",
  "updatedAt": "2026-07-20T09:00:00Z",
  "activeKeyCount": 0,
  "limitAmount": 1000,
  "usedAmount": 0
}

Response fields

FieldTypeDescription
id, organizationIdstringAccount UUID and organization UUID
namestringAccount name, unique within the organization
descriptionstringOptional; omitted when empty
statusstringactive or archived
tokenValidAfterstringOptional token validity boundary, UTC RFC 3339
createdBystringID of the creating principal
createdAt, updatedAtstringUTC RFC 3339 timestamps
activeKeyCountint32Number of active, unexpired long-lived keys
lastUsedAtstringLast successful use; omitted if never used
limitAmountfloat64Credits limit for the current organization billing period
usedAmountfloat64Credits used in that billing period

2. List service accounts

GET /v1/organizations/{organization_id}/service-accounts The response contains serviceAccounts (array of account objects), maxResults (int32), and optional nextToken. Empty results use []. A final page may contain records; stop when nextToken is absent, not when the array is empty.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID

Query parameters

ParameterTypeRequiredDescription
statusstringNoactive or archived; omit to include either status
keywordstringNoCase-insensitive keyword search across name and description; trimmed, up to 500 characters; Default: None
maxResultsintegerNoPage size, maximum 100; Default: 20
nextTokenstringNoCursor from the previous response; Default: None
If you omit periodStart and periodEnd, the API selects the latest available period. When a period is available, the list includes an archived account only if its usage was greater than zero in the selected period. See List service accounts for a usage period.

Success response (200 OK)

{
  "serviceAccounts": [
    {
      "id": "019c1234-5678-7abc-8def-012345678901",
      "organizationId": "019c1234-5678-7abc-8def-012345678900",
      "name": "CI Automation",
      "description": "Production CI jobs",
      "status": "active",
      "createdBy": "019c1234-5678-7abc-8def-012345678904",
      "createdAt": "2026-07-20T09:00:00Z",
      "updatedAt": "2026-07-20T09:00:00Z",
      "activeKeyCount": 1,
      "lastUsedAt": "2026-07-20T09:30:00Z",
      "limitAmount": 1000,
      "usedAmount": 125.5
    }
  ],
  "maxResults": 20
}

3. Get service account details

GET /v1/organizations/{organization_id}/service-accounts/{service_account_id} Get returns 404 if the account does not exist or belongs to another organization.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Success response (200 OK)

Returns a service account object. See Create service account for the response example and fields.

4. Update service account

PUT /v1/organizations/{organization_id}/service-accounts/{service_account_id}

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Request body

Update requires at least one of name, description, or limitAmount; omitted fields retain their values.
{
  "name": "CI Production",
  "description": "",
  "limitAmount": 1500
}
ParameterTypeRequiredDescription
namestringNoTrimmed; 1–100 characters; unique within the organization
descriptionstringNoOptional, trimmed, up to 500 characters; an empty string clears it on update
limitAmountfloat64NoOptional: -1 means no independent limit; 0 freezes new Credits consumption; a finite positive value caps consumption. Defaults to -1 on create

Success response (200 OK)

Returns a service account object. See Create service account for the response example and fields.

5. Archive service account

POST /v1/organizations/{organization_id}/service-accounts/{service_account_id}/archive Archive requires no body, changes status to archived, and revokes all remaining long-lived keys. Archived accounts cannot create or rotate keys. There is no unarchive operation. Repeated archive calls succeed without repeating the state change. Historical usage remains queryable.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Success response (200 OK)

Returns the archived service account object: status is archived, activeKeyCount is 0, and tokenValidAfter is the archive time. See Create service account for the remaining field definitions.

6. Create long-lived key

POST /v1/organizations/{organization_id}/service-accounts/{service_account_id}/keys Plaintext token is returned only when a key is created or rotated. Save it in a server-side secret store immediately; never put it in URLs, logs, source code, or error messages. Lists return only tokenDisplay. Default limits are 100 active accounts per organization, 5 active unexpired keys per account, and 500 active unexpired keys per organization. Organization configuration can override these limits; revoked or expired keys do not count.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Request body

{
  "name": "CI Production",
  "scope": "<space-separated-scopes>",
  "expiresAt": "2027-07-20T00:00:00Z"
}
ParameterTypeRequiredDescription
namestringYesTrimmed; 1–100 characters
scopestringYesSpace-separated scopes; no empty or duplicate scopes; up to 20 scopes, each up to 100 characters. Confirm available scopes with the Qoder implementation team
expiresAtstringNoRFC 3339 timestamp strictly later than the current time

Success response (201 Created)

{
  "id": "019c1234-5678-7abc-8def-012345678902",
  "organizationId": "019c1234-5678-7abc-8def-012345678900",
  "serviceAccountId": "019c1234-5678-7abc-8def-012345678901",
  "name": "CI Production",
  "tokenDisplay": "<masked_service_account_key>",
  "scope": "<space-separated-scopes>",
  "status": "active",
  "expiresAt": "2027-07-20T00:00:00Z",
  "createdBy": "019c1234-5678-7abc-8def-012345678904",
  "createdAt": "2026-07-20T09:00:00Z",
  "updatedAt": "2026-07-20T09:00:00Z",
  "isExpired": false,
  "token": "<service_account_key>"
}

Response fields

FieldTypeDescription
id, organizationId, serviceAccountIdstringKey, organization, and account UUIDs
name, scopestringKey name and space-separated permissions
tokenDisplaystringMasked value; cannot authenticate requests
statusstringactive or revoked
expiresAtstringOptional expiration timestamp; omitted when unset
lastUsedAtstringOptional last successful use timestamp; omitted if never used
createdBystringID of the creating principal
createdAt, updatedAtstringUTC RFC 3339 timestamps
isExpiredbooleanWhether the key has expired
tokenstringReturned only on create or rotate
status=active does not guarantee usability: a key with isExpired=true cannot authenticate.

7. List long-lived keys

GET /v1/organizations/{organization_id}/service-accounts/{service_account_id}/keys List accepts optional status (active or revoked, all by default), maxResults (default 20, max 100), and nextToken. It returns keys (array of key resources without token), maxResults, and optional nextToken; an empty list is [].

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Query parameters

ParameterTypeRequiredDescription
statusstringNoactive / revoked; all by default
maxResultsintegerNoDefault 20, maximum 100
nextTokenstringNoCursor from the preceding response

Success response (200 OK)

{
  "keys": [
    {
      "id": "019c1234-5678-7abc-8def-012345678902",
      "organizationId": "019c1234-5678-7abc-8def-012345678900",
      "serviceAccountId": "019c1234-5678-7abc-8def-012345678901",
      "name": "CI Production",
      "tokenDisplay": "<masked_service_account_key>",
      "scope": "<space-separated-scopes>",
      "status": "active",
      "expiresAt": "2027-07-20T00:00:00Z",
      "createdBy": "019c1234-5678-7abc-8def-012345678904",
      "createdAt": "2026-07-20T09:00:00Z",
      "updatedAt": "2026-07-20T09:00:00Z",
      "isExpired": false
    }
  ],
  "maxResults": 20
}

8. Rotate long-lived key

POST /v1/organizations/{organization_id}/service-accounts/{service_account_id}/keys/{key_id}/rotate Rotate requires no body. It creates a key inheriting the original name, scopes, and expiration, revokes the old key, and returns the new plaintext token once. Revoked or expired keys cannot rotate. For an overlap during migration, create a separate key, switch callers, then revoke the old key.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID
key_idstringYesKey ID

Success response (201 Created)

Returns a new key object with its one-time plaintext token. See Create long-lived key for the response example and fields.

9. Revoke long-lived key

DELETE /v1/organizations/{organization_id}/service-accounts/{service_account_id}/keys/{key_id} Revoke requires no body and returns 204 No Content; repeating it for the same key succeeds. Revoked keys cannot authenticate or rotate.

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID
key_idstringYesKey ID

Success response (204 No Content)

Returns 204 No Content with no response body.

10. Issue short-lived token

POST /v1/organizations/{organization_id}/service-accounts/{service_account_id}/tokens Authenticate with the target account's own long-lived key:

Path parameters

ParameterTypeRequiredDescription
organization_idstringYesOrganization ID
service_account_idstringYesService account ID

Request body

{
  "scope": "<space-separated-scopes>",
  "expirationSeconds": 3600,
  "customId": "deployment-2026-07-20"
}
ParameterTypeRequiredDescription
scopestringYesNonempty, space-separated subset of the source key's scopes; at most 2,048 characters and 20 distinct scopes, each up to 100 characters
expirationSecondsintegerNoOmitted or 0: default 3,600 seconds. Otherwise 1–43,200 seconds, capped by the source key's remaining lifetime
customIdstringNo1–128 ASCII characters; first character alphanumeric, subsequent characters alphanumeric or ._:/@+-; used for audit attribution
Only these three fields are accepted; unknown fields, explicit null, and incorrect types are rejected.

Success response (200 OK)

{
  "accessToken": "<opaque_service_account_token>",
  "tokenType": "Bearer",
  "expiresIn": 3600,
  "expiresAt": "2026-07-20T10:00:00Z"
}
accessToken, tokenType, and expiresAt are strings; expiresIn is an integer in seconds. Success includes Cache-Control: no-store and Pragma: no-cache. Each successful call issues a new token and is not idempotent. Use the token as an opaque credential; do not parse its internals or log it. Revoking the source key or archiving the account invalidates its credentials. Already-issued tokens may remain accepted until the gateway's credential-status cache refreshes, or until token expiry, whichever comes first. Do not assume instantaneous token revocation; confirm the cache interval for your deployment.

Usage examples

Create service account

curl -X POST "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts" \
  -H "Authorization: Bearer <organization_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI Automation","description":"Production CI jobs","limitAmount":1000}'

List service accounts

curl -X GET "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts?maxResults=20" \
  -H "Authorization: Bearer <organization_api_key>"

Get service account details

curl -X GET "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901" \
  -H "Authorization: Bearer <organization_api_key>"

Update service account

curl -X PUT "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901" \
  -H "Authorization: Bearer <organization_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI Production","description":"","limitAmount":1500}'

Archive service account

curl -X POST "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/archive" \
  -H "Authorization: Bearer <organization_api_key>"

Create long-lived key

curl -X POST "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/keys" \
  -H "Authorization: Bearer <organization_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI Production","scope":"<space-separated-scopes>","expiresAt":"2027-07-20T00:00:00Z"}'

List long-lived keys

curl -X GET "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/keys?maxResults=20" \
  -H "Authorization: Bearer <organization_api_key>"

Rotate long-lived key

curl -X POST "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/keys/019c1234-5678-7abc-8def-012345678902/rotate" \
  -H "Authorization: Bearer <organization_api_key>"

Revoke long-lived key

curl -X DELETE "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/keys/019c1234-5678-7abc-8def-012345678902" \
  -H "Authorization: Bearer <organization_api_key>"

Issue short-lived token

curl -X POST "https://api.qoder.com/v1/organizations/019c1234-5678-7abc-8def-012345678900/service-accounts/019c1234-5678-7abc-8def-012345678901/tokens" \
  -H "Authorization: Bearer <service_account_key>" \
  -H "Content-Type: application/json" \
  -d '{"scope":"<space-separated-scopes>","expirationSeconds":3600,"customId":"deployment-2026-07-20"}'

Error codes

Errors contain requestId, code, and message, with optional details:
{
  "requestId": "req_abc123",
  "code": "BadRequest",
  "message": "name is required"
}
HTTPExample codesMeaning
400BadRequest, ServiceAccountLimitAmountInvalid, ServiceAccountArchived, ServiceAccountKeyScopeUnsupported, ServiceAccountKeyLimitExceeded, OrganizationServiceAccountKeyLimitExceededInvalid fields, state, scope, or configured limit
401UnauthorizedMissing, invalid, or expired credential
403Forbidden, OrganizationPlanCapabilityForbiddenWrong credential principal, organization, scope, or disabled capability
404NotFoundAccount or key absent or outside the organization
409ServiceAccountNameAlreadyExists, ServiceAccountKeyAlreadyExistsAccount name or key conflict
500InternalErrorInternal failure
503InternalError or a dependency-specific codeToken issuer or dependency unavailable
Business-specific codes are preserved. Handle the HTTP status and returned code; do not assume every validation error is BadRequest or every conflict is AlreadyExists. Use Usage API for historical events and net Credits.