serviceAccounts
Service accounts are identities for machines: a deploy pipeline, a billing worker, a partner integration.
Service accounts are identities for machines: a deploy pipeline, a billing worker, a partner integration. They live in
a tenant's directory next to people (kind: 'service'), receive access the same way through role bindings, groups,
and relationships, and authenticate only with API keys, never by signing in.
Giving each integration its own account keeps its access reviewable and revocable without touching anyone's personal
access.
How service accounts relate to identities
This group is a focused view of the identity directory: it only ever returns or changes identities of kind
service, and it is authorized with the same iam:identities:* actions as
identities, checked on the tenant for create and list and on iam/{identityId}
for everything else. When an account calls
the API with a key, policies see it as principal.kind: 'service', so a statement can treat machines differently
from people. Bind roles to an account with
bindings.create, and remove one completely with
identities.offboard when you need a successor for the resources it owns.
Scheduled deactivation
An expiresAt (epoch milliseconds, at most ten years ahead) gives an account a deadline, which suits a vendor
integration or a migration job. From that instant every key of the account is refused. The purge worker
(iam.purgeDeleted(), see scheduled jobs) then disables the account, deletes its keys, and
records identity:expire. If you extend or clear the deadline before the worker runs, the existing keys work again;
after it has run, extend or clear the deadline, enable the account with setStatus, and issue new
keys. See
time-bound identities.
| Method | What it does | Access |
|---|---|---|
create | Creates a service account in the tenant, optionally with a date after which it is deactivated. | Credential |
delete | Deletes a service account, revoking its keys and removing every grant it held. | Credential |
get | Returns one service account by id. | Credential |
list | Lists the tenant's service accounts, without deleted ones unless you ask for them. | Credential |
setStatus | Disables a service account, ending all its access at once, or enables it again. | Credential |
update | Renames a service account, changes its description or directory attributes, or schedules or clears its deactivation. | Credential |
create
Creates a service account in the tenant, optionally with a date after which it is deactivated.
- Permission:
iam:identities:createon the tenant, and the caller must hold an active grant authority. - Audited as:
iam:identities:create. - Errors:
GRANT_AUTHORITY_REQUIREDwhen the caller holds no grant authority;LIMIT_EXCEEDEDat the tenant's service account limit;INVALID_INPUTfor an empty name, a description over 512 characters, or anexpiresAtthat is not in the future or is more than ten years away.
The new account is active but holds no access until you bind roles to it or add it to groups. Automatic
access package rules are evaluated for it right after creation, so
a rule that matches service accounts grants its package at once. Then issue a key with
credentials.create.
const account = await iam.api.serviceAccounts.create(credential, {
tenantId,
name: 'Billing sync',
description: 'Nightly export to the finance system',
expiresAt: Date.parse('2027-06-30T00:00:00Z'),
});Prop
Type
A Identity object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/create" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"name": "<name>"
}'iam.api.serviceAccounts.create(
credential: CredentialInput,
input: { tenantId: string; name: string; description?: string; expiresAt?: number },
): Promise<Identity>delete
Deletes a service account, revoking its keys and removing every grant it held.
- Permission:
iam:identities:deleteon the account, with recent authentication. - Audited as:
iam:identities:delete, plusidentity:deletewithmetadata.kindset toservice. - Errors:
NOT_FOUNDwhen the id is not a service account of this tenant;CONFLICTwhen it is already deleted;INVALID_INPUTwhen an account tries to delete itself;RECENT_AUTH_REQUIRED;IMPERSONATION_RESTRICTED.
In one transaction the account's keys and assumed-role sessions end, its role bindings, group memberships,
activations, package assignments, relationships, and boundary are removed, pending access requests are cancelled, and
any grant authority it held is revoked. The account stays as a deleted record, visible with list({ includeDeleted }),
so the audit log keeps resolving its id.
Prop
Type
A PublicIdentity object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/delete" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.serviceAccounts.delete(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<PublicIdentity>get
Returns one service account by id.
- Permission:
iam:identities:readon the account. - Audited as:
iam:identities:read. - Errors:
NOT_FOUNDwhen the id is not a service account of this tenant (people are not returned here).
A deleted account is still returned, with status: 'deleted'.
Prop
Type
A PublicIdentity object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/get" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.serviceAccounts.get(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<PublicIdentity>list
Lists the tenant's service accounts, without deleted ones unless you ask for them.
- Permission:
iam:identities:readon the tenant. - Audited as:
iam:identities:read.
Pass includeDeleted: true to include deleted accounts, for example when resolving old audit entries. Combine it with
credentials.list to review which accounts hold keys and when they were
last used.
Prop
Type
An array of PublicIdentity.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/list" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.serviceAccounts.list(
credential: CredentialInput,
input: { tenantId: string; includeDeleted?: boolean },
): Promise<PublicIdentity[]>setStatus
Disables a service account, ending all its access at once, or enables it again.
- Permission:
iam:identities:updateon the account, with recent authentication. - Audited as:
iam:identities:update. - Errors:
INVALID_TRANSITION(409) when enabling an account whoseexpiresAthas passed;NOT_FOUNDwhen the id is not a service account of this tenant or was deleted;INVALID_INPUTfor a status other thanactiveordisabled;INVARIANT_VIOLATIONwhen an enforced access invariant would newly fail;RECENT_AUTH_REQUIRED;IMPERSONATION_RESTRICTED.
Disabling deletes every API key the account holds and every role session it assumed, while its bindings and
memberships stay in place. Use it to contain a leaked key or pause an integration. Enabling the account again does not
bring the keys back: issue new ones. To re-enable an expired account, first extend or clear expiresAt with
update, so nobody quietly turns a finished integration back on.
Prop
Type
A PublicIdentity object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/setStatus" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>",
"status": "active"
}'iam.api.serviceAccounts.setStatus(
credential: CredentialInput,
input: { tenantId: string; identityId: string; status: 'active' | 'disabled' },
): Promise<PublicIdentity>update
Renames a service account, changes its description or directory attributes, or schedules or clears its deactivation.
- Permission:
iam:identities:updateon the account. - Audited as:
iam:identities:update. - Errors:
INVALID_INPUTwhen nothing is given to change, an attribute is not declared inpermissions.identityAttributesor has the wrong type, orexpiresAtis invalid;NOT_FOUNDwhen the id is not a service account of this tenant or was deleted;INVARIANT_VIOLATIONwhen an enforced access invariant would newly fail.
attributes replaces the account's whole attribute set; policies read them as principal.{name}. Pass
expiresAt: null to make the account permanent. Automatic access package rules are re-evaluated for the account
afterwards, so an attribute change can add or remove package access.
Prop
Type
A PublicIdentity object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/serviceAccounts/update" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.serviceAccounts.update(
credential: CredentialInput,
input: {
tenantId: string;
identityId: string;
name?: string;
description?: string;
attributes?: Record<string, Json>;
expiresAt?: number | null;
},
): Promise<PublicIdentity>Better IAM is created by Sean Filimon
Last updated