Identity & Access Management

Preview — pending review

This reference is generated from the platform API contract and is still under review. Operations, fields, and descriptions can change before publication.

Create a service account (optionally minting an initial key)

POST
/scopes/{scopeId}/iam/service-accounts

Key scopes: with createKey: true the initial key carries keyScopes (optional; omitted = the default key scopes, i.e. the whole ApiTokenScope catalogue minus iam:write). An explicit empty list is 422 VALIDATION_FAILED (field keyScopes); iam:write is never grantable to a service-account key (422 VALIDATION_FAILED, field keyScopes, details.reason iam_write_not_grantable_to_service_account_key). keyScopes is ignored when createKey is false. Token callers: createKey: true from a pc_live_ token may only request keyScopes (explicit or defaulted) the token holds itself -- otherwise 403 FORBIDDEN TOKEN_SCOPE_MISSING (missingTokenScopes), no account created. A key has no IP allowlist, so a token with an ipAllowlist cannot mint one (403 TOKEN_FENCE_EXCEEDED); a key minted by an expiring token expires with it. Initial roleIds stay capped by the creator's current role. A service-account key calling this operation gets 403 INTERACTIVE_SESSION_REQUIRED (an SA key never performs an IAM write). Creates a new machine identity in the active scope from the service account creation form: a lowercase DNS-label account name (becomes the immutable local part of the email-style ID), an optional description, an optional set of initial role bindings from the role catalog scoped to the current project, and an optional "create a key and download the credential" flag. When createKey is true the response carries the one-time private-key credential exactly once; it can never be retrieved again. On acceptance the account becomes discoverable in the scoped list. Validation: name must match ^[a-z]([a-z0-9-]{1,28}[a-z0-9])?$ (3–30 chars, lowercase DNS label) else 422 VALIDATION_FAILED; name must be unique within the tenant else 409 CONFLICT ("An account with this name already exists"); each entry in roleIds must exist in the role catalog else 422; caller must hold an IAM admin-capable role (owner|admin) in the scope else 403; per-scope service-account quota is enforced → 409 QUOTA_EXCEEDED. Async & state: Asynchronous create — returns 202 with the resource in its transitional enabled state while provisioning completes (or 201 when provisioning is immediate). Emits resource.updated SSE frames (kind: iam.service_account, patch: {status, keyCount, bindingCount}) as the account, optional initial key and bindings materialize. Errors: 401 unauthenticated; 403 caller lacks create permission; 404 scope not found; 409 duplicate name (CONFLICT) or quota exceeded (QUOTA_EXCEEDED); 422 invalid name/description/roleIds; 500 provisioning failure. Idempotency/Concurrency: Idempotency-Key required; replaying the same key returns the original result (including the same one-time credential envelope) without creating a duplicate account.

Step-up: with createKey: true or an owner-ranked roleIds entry, a console session whose account has two-factor authentication needs the factor verified in the last 5 minutes, otherwise 403 MFA_STEP_UP_REQUIRED (verify with POST /auth/step-up, then retry).

Authorization

bearerToken
headerAuthorizationBearer <token>

An API token (pc_live_…) or a service-account key (sak_live_…), created in Identity & Access Management and sent as Authorization: Bearer <token>. Requests are rate limited per credential and per tenant: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a refused request gets 429 RATE_LIMITED with Retry-After.

Path Parameters

scopeId*string

Opaque ownership+authorization scope resolving to tenant + subscription + project.

Header Parameters

Idempotency-Key*string

Required: this operation creates a new resource asynchronously, so a retry without the key would create a second one. Same key+body within 24h replays the original result; the same key with a different body is 409 CONFLICT. Keys are unique per tenant (the same key in two tenants is two different requests). An API token or service-account (bearer) caller MUST send it -- a request without it is refused with 400 IDEMPOTENCY_KEY_REQUIRED (ErrIdempotencyKeyRequired); reuse the same key on every retry of one logical request. A console session gets one from the edge.

Formatuuid

Request Body

application/json
  1. body

The service account creation form payload.

name*string

Lowercase DNS label, 3-30 chars; becomes the local part of the email-style ID and cannot be changed later.

Match^[a-z]([a-z0-9-]{1,28}[a-z0-9])?$
description?string

What the account is for and who owns it; shown in lists.

Lengthlength <= 255
roleIds?array<string>

Initial role bindings granted on the current project, chosen from the role catalog.

createKey?boolean

When true, mint an initial key and return its one-time credential in the response.

Defaultfalse
keyScopes?array<>

Scopes of the initial key -- optional; omitted with createKey: true = the default key scopes (the ApiTokenScope catalogue minus iam:write); an explicit empty list is 422; ignored when createKey is false. iam:write is never grantable to a service-account key (422).

Response Body

Service account created synchronously. credential is present only when createKey was true — it is shown exactly once.

application/json
  1. response

Create response. credential is present only when createKey was true and is shown exactly once.

serviceAccount*

Machine identity with email-style ID, long-lived keys and scoped role bindings.

key?

Long-lived key metadata. Private key material is never present here — it is revealed exactly once at creation.

credential?

One-time private-key credential JSON. Returned ONLY by service-account create (with createKey) and key create; never retrievable afterwards and never present in GET/list/export responses.

curl -X POST "https://example.com/scopes/string/iam/service-accounts" \  -H "Idempotency-Key: 497f6eca-6276-4993-bfeb-53cbbbba6f08" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "serviceAccount": {    "id": "string",    "name": "string",    "email": "string",    "arn": "string",    "uniqueId": "string",    "description": "string",    "status": "enabled",    "createdAt": "2019-08-24T14:15:22Z",    "createdBy": "string",    "lastUsedAt": "2019-08-24T14:15:22Z",    "keyCount": 0,    "bindingCount": 0,    "oldestKeyAgeDays": 0,    "auth24h": 0,    "denied24h": 0  },  "key": {    "id": "string",    "fingerprint": "string",    "createdAt": "2019-08-24T14:15:22Z",    "lastUsedAt": "2019-08-24T14:15:22Z",    "expiresAt": "2019-08-24T14:15:22Z",    "status": "active",    "ageDays": 0,    "scopes": [      "compute:read"    ]  },  "credential": {    "type": "service_account",    "account": "string",    "account_id": "string",    "private_key_id": "string",    "private_key": "string",    "secret": "string",    "token_uri": "string",    "auth_provider": "string",    "universe_domain": "string",    "region": "string"  }}