Skip to main content

API keys, service accounts and jobs endpoints

Programmatic management of credentials and reading of long-running asynchronous jobs.

Endpoint table

API keys

MethodPathPermission
POST/platform/organizations/{organization_id}/api-keysmember:manage
GET/platform/organizations/{organization_id}/api-keysSession
GET/platform/organizations/{organization_id}/api-keys/{key_id}Session
PATCH/platform/organizations/{organization_id}/api-keys/{key_id}member:manage
POST/platform/organizations/{organization_id}/api-keys/{key_id}/revokemember:manage
POST/platform/organizations/{organization_id}/api-keys/{key_id}/rotatemember:manage
GET/platform/organizations/{organization_id}/api-keys/{key_id}/usageSession

Service accounts

MethodPathPermission
POST/platform/organizations/{organization_id}/service-accountsmember:manage
GET/platform/organizations/{organization_id}/service-accountsSession
GET/platform/organizations/{organization_id}/service-accounts/{sa_id}Session
PATCH/platform/organizations/{organization_id}/service-accounts/{sa_id}member:manage

Asynchronous jobs

MethodPathPermission
GET/platform/organizations/{organization_id}/jobsSession
GET/platform/organizations/{organization_id}/jobs/{job_id}Session

API keys

POST /.../api-keys

Creates a new key. The full value (raw_key) appears only once in this response — save immediately.

Request body (ApiKeyCreateRequest):

{
"name": "CI pipeline",
"description": "SOC deploy pipeline",
"scopes": ["analysis:run", "projects:read", "cases:write"],
"expires_in_days": 365,
"project_id": "prj_01H…"
}
  • scopes[] — accepted values (see Authentication): projects:read, projects:write, rulesets:read, rulesets:write, analysis:run, cases:read, cases:write, reviews:read, reviews:write, versions:read, versions:write, admin:*. Unknown scopes → 422.
  • expires_in_days — 1 to 3650 (10 years). null for no expiration.
  • project_id — optional; if set, the key only works on the given project.

Response 201 (ApiKeyCreatedResponse):

{
"key": {
"id": "key_01H…",
"name": "CI pipeline",
"description": "SOC deploy pipeline",
"organization_id": "org_01H…",
"project_id": "prj_01H…",
"scopes": ["analysis:run", "projects:read", "cases:write"],
"key_prefix": "rfk_01H2XA9C",
"is_active": true,
"last_used_at": null,
"expires_at": "2027-04-24T00:00:00Z",
"created_by": "usr_01H…",
"created_at": "2026-04-24T15:30:00Z",
"updated_at": "2026-04-24T15:30:00Z"
},
"raw_key": "rfk_01H2XA9CKEB7WZG8RMN2KPQV7_e6c4f2abcd…"
}

Emits webhook: api_key.created.

GET /.../api-keys

Lists the organization's keys.

Response 200: array of ApiKeyRecord (without raw_key).

GET /.../api-keys/{key_id}

Fetches a specific key.

PATCH /.../api-keys/{key_id}

Partial update (name, description, scopes). Does not allow re-generating the value — use rotate for that.

POST /.../api-keys/{key_id}/revoke

Revokes the key immediately. Irreversible.

Request body (ApiKeyRevokeRequest):

{ "reason": "Manually rotated after leak" }

Response 200: ApiKeyRecord with is_active=false. Emits webhook api_key.revoked.

POST /.../api-keys/{key_id}/rotate

Generates a new secret keeping the id and scopes. The old value stops being accepted immediately.

Response 200: ApiKeyCreatedResponse (with a new raw_key — save it!).

GET /.../api-keys/{key_id}/usage

Usage history for the key (last N calls). Useful to audit where the key is being used.

Response 200: array of ApiKeyUsageRecord:

[
{
"id": "usage_...",
"key_id": "key_01H…",
"endpoint": "/api/v1/analysis/validate",
"method": "POST",
"status_code": 200,
"duration_ms": 142.7,
"request_id": "…",
"ip_address": "203.0.113.10",
"user_agent": "curl/8.4",
"created_at": "2026-04-24T14:00:00Z"
}
]

Service accounts

A service account = identity of a system (not a human) with a role inside the organization. Used as the "owner" of resources created by automated integrations — audit trail stays clear.

POST /.../service-accounts

{
"name": "CI Bot",
"description": "Robot that publishes versions from Git",
"role": "engineer"
}

Response 201: ServiceAccountRecord with slug and role.

GET /.../service-accounts

Lists service accounts.

PATCH /.../service-accounts/{sa_id}

Updates name, description, role or is_active.

Asynchronous jobs

Long-running tasks (batch logtest, Git sync, pipeline poll, secret rotation) are queued as jobs and executed in the background. The client polls the state via the endpoints below.

GET /.../jobs

Lists the organization's jobs.

Response 200: array of JobRecord:

[
{
"id": "job_...",
"organization_id": "org_...",
"project_id": "prj_...",
"type": "batch_regression",
"status": "running",
"progress": 12,
"total": 50,
"created_by": "usr_...",
"started_at": "2026-04-24T15:00:00Z",
"completed_at": null,
"error_message": null,
"created_at": "2026-04-24T14:59:58Z",
"updated_at": "2026-04-24T15:01:12Z"
}
]

typeimport_export.

statuscancelled.

GET /.../jobs/{job_id}

Returns JobResultRecord, which is JobRecord + a result field (dictionary with the task output when completed).

Recommended polling:

import time, httpx

def wait_for_job(client, org_id, job_id, timeout=600):
deadline = time.time() + timeout
while time.time() < deadline:
r = client.get(f"{base}/platform/organizations/{org_id}/jobs/{job_id}")
r.raise_for_status()
job = r.json()
if job["status"] in ("completed", "failed", "cancelled"):
return job
time.sleep(2)
raise TimeoutError("Job took too long")

Shared structures

ApiKeyRecord

{
"id": "string",
"name": "string",
"description": "string",
"organization_id": "string",
"project_id": "string|null",
"scopes": ["string"],
"key_prefix": "rfk_XXXXXXXX",
"is_active": true,
"last_used_at": "ISO 8601|null",
"expires_at": "ISO 8601|null",
"created_by": "string|null",
"created_at": "ISO 8601",
"updated_at": "ISO 8601"
}

key_prefix is the first 12 chars of raw_key. The full secret is never exposed outside the create/rotate moment.

ApiKeyUsageRecord

{
"id": "string",
"key_id": "string",
"endpoint": "string",
"method": "string",
"status_code": 200,
"duration_ms": 123.4,
"request_id": "string|null",
"ip_address": "string|null",
"user_agent": "string|null",
"created_at": "ISO 8601"
}

JobRecord

See above. JobResultRecord = JobRecord + result: object.