keys
Key management (KMS) gives each tenant cryptographic keys that never leave the server.
Key management (KMS) gives each tenant cryptographic keys that never leave the server. Applications call encrypt,
decrypt, sign, verify and the MAC methods with a key, and every call is authorized by policy and written to the
audit trail. It works like AWS KMS: keys have versions and rotate without breaking existing data, aliases let
applications switch keys without a deploy, encryption contexts bind ciphertexts to their records, data keys make
envelope encryption possible, and grants hand one workload the use of one key. The repository guide is
docs/key-management.md.
Key kinds
aes-256-gcm(the default):encryptanddecryptup to 4 KiB,generateDataKey,reEncrypt.hmac-sha256,hmac-sha384,hmac-sha512:generateMac,verifyMac, and HS256/384/512 tokens withsignJwt.ecc-p256,ecc-p384,ed25519: ES256, ES384 and EdDSA signatures and tokens.rsa-2048,rsa-3072,rsa-4096: withkeyUsage: 'sign', PS256-PS512 and RS256-RS512; withkeyUsage: 'encrypt', RSA-OAEP with SHA-256 for small values.
A tenant keeps at most 1000 keys, and a key at most 1000 versions, 50 aliases and 50 grants.
Resources and conditions
Keys are iam/kms/{keyId}. create and tenant-wide listAliases use iam/kms. Conditions can read
resource.keySpec, resource.keyUsage, resource.keyState, resource.aliases, resource.createdBy, each tag as
resource.tags.{key}, the call's encryption context as resource.encryptionContext.{key} and
resource.encryptionContextKeys, and the algorithm of signing, MAC and token calls as resource.algorithm. For
create, these attributes describe the requested key, so a policy can require that new keys carry the caller's
team tag. Sessions that view as someone else (impersonation) are refused.
Managed keys
Keys a certificate authority signs with or a data protection profile encrypts with carry managedBy (pki or
protection, also resource.managedBy) and managedId. Every cryptographic call and createGrant refuse them with
KEY_MANAGED (409), so only the module that manages a key uses it. They can still be read, tagged, rotated, disabled
and scheduled for deletion.
Ciphertexts and encryption contexts
A ciphertext looks like kms1.{keyId}.{version}.{t|b}.{payload}. It names its key and version, so decrypt needs
only the ciphertext and the encryption context. The encryption context is up to 16 non-secret string pairs that are
authenticated with the ciphertext. Decrypting with a different context, or decrypting a modified ciphertext, fails
with INVALID_CIPHERTEXT, and that failure is audited as a denied decrypt.
Grants
A grant lets one identity (a person, service account or agent) perform named operations on one key: read,
encrypt, decrypt, generate-data-key, sign, verify, generate-mac or verify-mac. The operations can be
limited to certain encryption contexts, and a grant can have an end date. A grant applies only when no policy decides
the call and every boundary, explicit deny, session policy and API-key scope allows it. Every use also checks that the
grant's creator could make the same call now, so a grant stops working when its creator loses the access it passed
on. Grants serve user sessions and API keys acting in their own right. Assumed roles, session tokens, delegated agent
sessions and impersonation never use them.
Aliases
Adding, moving or removing an alias changes resource.aliases, so like a change of tags it must not give the caller
any KMS action on the keys involved that they are refused now. Alias names are authorized on their own: creating,
moving or removing alias/{name} needs iam:kms:update on
iam/kms/alias/{name} as well as on the key, so managing one key does not let anyone claim a name others encrypt
under.
| Method | What it does | Access |
|---|---|---|
cancelDeletion | Cancels a scheduled deletion. The key comes back disabled. | Credential |
create | Creates a key, optionally with an alias, tags, a description and automatic rotation. | Credential |
createAlias | Names a key alias/{name}. Names use letters, digits, slashes, underscores and hyphens and are unique within the tenant. | Credential |
createGrant | Allows an identity to perform named operations on one key, optionally until expiresAt and, for encryption operations, only with a matching encryption context (constraints.encryptionContextEquals or encryptionContextSubset). | Credential |
decrypt | Decrypts a ciphertext from encrypt, generateDataKey or reEncrypt, given the same encryption context. Text comes back as plaintext, bytes as plaintextBase64. Passing keyId additionally insists on that key. | Credential |
deleteAlias | Removes an alias. The key stays. | Credential |
disable | Disables a key. Every cryptographic call with it is refused with KEY_STATE_INVALID until it is enabled again, so data encrypted under it cannot be read in the meantime. | Credential |
enable | Enables a disabled key. | Credential |
encrypt | Encrypts up to 4 KiB (plaintext as UTF-8 text, or plaintextBase64) with the key's current version, bound to the optional encryption context. RSA encryption keys accept what their modulus allows (190 bytes for 2048 bits). | Credential |
generateDataKey | Returns a fresh random data key of 16, 24, 32 (default) or 64 bytes for encrypting large data locally: the plaintext key to use and discard, and its ciphertext under the KMS key to store beside the data. With includePlaintext: false only the ciphertext comes back. | Credential |
generateMac | Computes an HMAC of a message with the key's current version and returns it as base64url. | Credential |
get | One key by id or alias: its spec, usage, state, current version, tags, aliases, algorithms and rotation schedule. Never its material. | Credential |
jwks | Every version's public key of an asymmetric key, as a JWK Set for verifying tokens signed with signJwt. | Credential |
list | The tenant's keys that the caller may read, newest first, optionally filtered by state or keyUsage. | Credential |
listAliases | Every alias in the tenant, or the aliases of one key (keyId), sorted by name. | Credential |
listGrants | The key's grants, newest first, each with active (not lapsed). | Credential |
listVersions | The key's versions, newest first, with their origin (create, rotate or automatic) and, for asymmetric keys, a fingerprint of the public key. | Credential |
publicKey | The public key of an asymmetric key version (the current one by default), as SPKI PEM and as a JWK whose kid is {keyId}.{version}. Anyone holding it can verify signatures without calling IAM. RSA encryption keys bind a label to their ciphertexts, so values encrypted offline with the public key are not KMS ciphertexts. | Credential |
reEncrypt | Decrypts a ciphertext and encrypts it again under another key or context, without the plaintext leaving the server. | Credential |
retireGrant | Gives up a grant made to the caller's own identity. It needs no permission, so a workload can drop access it no longer uses. | Credential |
revokeGrant | Revokes a grant. | Credential |
rotate | Creates a new version on demand. The new version encrypts, signs and computes MACs from now on, and older versions keep decrypting and verifying. | Credential |
scheduleDeletion | Schedules the key's destruction after a waiting period of 7 to 30 days (30 by default). The key is unusable while it waits. Afterwards the scheduler job iam.kms.maintain() destroys its material, aliases and grants, and nothing encrypted under it can be decrypted again. | Credential |
sign | Signs a message (message or messageBase64, up to 64 KiB) with the key's current version. algorithm defaults to the key's first (ES256, ES384, EdDSA, or PS256 for RSA). ECDSA signatures are DER unless format: 'jose'. | Credential |
signJwt | Signs a JWT with a signing key or a MAC key. The header is { alg, kid, typ } with kid = {keyId}.{version} (the same kid as in jwks). iat is added when absent, and exp comes from expiresInSeconds (1 second to 1 year). | Credential |
update | Changes the description, replaces the tags, or sets or clears (null) the automatic rotation period. | Credential |
updateAlias | Points an alias at another key of the same kind and usage, so applications that name the alias switch keys without a deploy. | Credential |
verify | Checks a signature from sign with the given keyVersion (the current one by default). Returns { valid }: a mismatch is not an error. | Credential |
verifyJwt | Verifies a JWT made with a key. The kid selects the version, alg must be one of the key's algorithms (so none and algorithm swaps fail), exp and nbf are checked with clockToleranceSeconds (60 by default), and audience and issuer are checked when given. Tokens with a crit header, or an exp or nbf that is not a number, are refused. Returns { valid: true, claims, header } or { valid: false, reason } with reason one of signature, expired, not-yet-valid, audience, issuer, algorithm, header or claims. | Credential |
verifyMac | Checks a MAC in constant time with the given keyVersion (the current one by default). Returns { valid }. | Credential |
cancelDeletion
Cancels a scheduled deletion. The key comes back disabled.
- Permission:
iam:kms:delete. - Errors:
KEY_STATE_INVALIDunless the key is pending deletion.
Prop
Type
A KeySummary 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/keys/cancelDeletion" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.cancelDeletion(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeySummary>create
Creates a key, optionally with an alias, tags, a description and automatic rotation.
Used inKey management
- Permission:
iam:kms:createoniam/kms. The request's spec, usage and tags are the resource attributes. - Audited as:
iam:kms:create(metadata:keyId,keySpec,keyUsage,alias). - Errors:
INVALID_INPUTfor an unknownkeySpec, a missing or wrongkeyUsage, invalid tags, a malformed alias or arotationPeriodDaysoutside 1 to 3650;CONFLICTwhen the alias is taken;LIMIT_EXCEEDEDpast 1000 keys.
const key = await iam.api.keys.create(credential, {
tenantId,
alias: 'alias/customer-records',
tags: { team: 'payments' },
rotationPeriodDays: 365,
});A KeyCreateInput object:
Prop
Type
A KeySummary 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/keys/create" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.keys.create(
credential: CredentialInput,
input: KeyCreateInput,
): Promise<KeySummary>createAlias
Names a key alias/{name}. Names use letters, digits, slashes, underscores and hyphens and are unique within the tenant.
- Permission:
iam:kms:updateon the key and oniam/kms/alias/{name}. - Errors:
CONFLICTwhen the alias is taken;LIMIT_EXCEEDEDpast 50 aliases on the key;INVALID_INPUTfor a malformed name, or whenkeyIdis itself an alias.
Prop
Type
A AliasSummary 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/keys/createAlias" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"alias": "<alias>",
"keyId": "<keyId>"
}'iam.api.keys.createAlias(
credential: CredentialInput,
input: { tenantId: string; alias: string; keyId: string },
): Promise<AliasSummary>createGrant
Allows an identity to perform named operations on one key, optionally until expiresAt and, for encryption operations, only with a matching encryption context (constraints.encryptionContextEquals or encryptionContextSubset).
Used inKey management
- Permission:
iam:kms:grantfrom a user session or API key of the key's tenant acting in its own right, and the caller must be allowed each granted operation by policy. Grants the caller holds do not count, and each use of the grant checks the creator's access again, so a grant never passes on more than its creator has. - Audited as:
iam:kms:grant(metadata:grantId,granteeType,granteeId,operations,constraints,expiresAt). Calls allowed through the grant record itsgrantId. - Errors:
INVALID_INPUTfor operations the key's usage does not support, constraints on operations without an encryption context, or agranteeTypeother thanidentity;ACCESS_DENIEDwhen the caller does not hold an operation or acts through a role, token or delegated session;NOT_FOUNDfor an unknown identity;LIMIT_EXCEEDEDpast 50 grants.
await iam.api.keys.createGrant(ownerCredential, {
tenantId,
keyId: 'alias/customer-records',
granteeId: billingWorker.id,
operations: ['decrypt'],
constraints: { encryptionContextSubset: { app: 'billing' } },
});Prop
Type
A GrantSummary 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/keys/createGrant" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>",
"granteeId": "<granteeId>",
"operations": [
"encrypt"
]
}'iam.api.keys.createGrant(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
granteeType?: 'identity';
granteeId: string;
operations: GrantOperation[];
constraints?: GrantConstraints;
expiresAt?: number;
name?: string;
},
): Promise<GrantSummary>decrypt
Decrypts a ciphertext from encrypt, generateDataKey or reEncrypt, given the same encryption context. Text comes back as plaintext, bytes as plaintextBase64. Passing keyId additionally insists on that key.
Used inKey management
- Permission:
iam:kms:decrypton the key the ciphertext names, or a grant. - Audited as:
iam:kms:decrypt(metadata:keyVersion,encryptionContext). A decryption that fails after the call was authorized is recorded as adenywithreason: 'invalid-ciphertext'. - Errors:
INVALID_CIPHERTEXTfor a malformed or modified ciphertext, a different encryption context, or a ciphertext of another key;KEY_STATE_INVALID;NOT_FOUNDwhen the key has been destroyed.
Prop
Type
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/keys/decrypt" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"ciphertext": "<ciphertext>"
}'iam.api.keys.decrypt(
credential: CredentialInput,
input: {
tenantId: string;
ciphertext: string;
encryptionContext?: EncryptionContext;
keyId?: string;
},
): Promise<{
plaintext?: string;
plaintextBase64?: string;
keyId: string;
keyVersion: number;
}>deleteAlias
Removes an alias. The key stays.
- Permission:
iam:kms:updateon the key the alias names and oniam/kms/alias/{name}.
Prop
Type
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/keys/deleteAlias" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"alias": "<alias>"
}'iam.api.keys.deleteAlias(
credential: CredentialInput,
input: { tenantId: string; alias: string },
): Promise<{ success: true }>disable
Disables a key. Every cryptographic call with it is refused with KEY_STATE_INVALID until it is enabled again, so data encrypted under it cannot be read in the meantime.
- Permission:
iam:kms:update. - Errors:
KEY_STATE_INVALIDfor a key pending deletion.
Prop
Type
A KeySummary 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/keys/disable" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.disable(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeySummary>enable
Enables a disabled key.
- Permission:
iam:kms:update. - Errors:
KEY_STATE_INVALIDfor a key pending deletion (cancel the deletion first).
Prop
Type
A KeySummary 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/keys/enable" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.enable(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeySummary>encrypt
Encrypts up to 4 KiB (plaintext as UTF-8 text, or plaintextBase64) with the key's current version, bound to the optional encryption context. RSA encryption keys accept what their modulus allows (190 bytes for 2048 bits).
Used inKey management
- Permission:
iam:kms:encrypt, or a grant. - Audited as:
iam:kms:encrypt(metadata:keyVersion,encryptionContext). - Errors:
KEY_STATE_INVALIDfor a disabled key or a key pending deletion;INVALID_INPUTfor a key that is not an encryption key or a plaintext that is too long.
const { ciphertext } = await iam.api.keys.encrypt(credential, {
tenantId,
keyId: 'alias/customer-records',
plaintext: 'card on file: 4242',
encryptionContext: { customer: 'cus_123' },
});Prop
Type
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/keys/encrypt" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.encrypt(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
plaintext?: string;
plaintextBase64?: string;
encryptionContext?: EncryptionContext;
},
): Promise<{ ciphertext: string; keyId: string; keyVersion: number }>generateDataKey
Returns a fresh random data key of 16, 24, 32 (default) or 64 bytes for encrypting large data locally: the plaintext key to use and discard, and its ciphertext under the KMS key to store beside the data. With includePlaintext: false only the ciphertext comes back.
Used inKey management
- Permission:
iam:kms:generate-data-key, or a grant. Decrypting the stored data key later needsiam:kms:decrypt. - Audited as:
iam:kms:generate-data-key(metadata:keyVersion,bytes,encryptionContext).
Prop
Type
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/keys/generateDataKey" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.generateDataKey(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
bytes?: 16 | 24 | 32 | 64;
encryptionContext?: EncryptionContext;
includePlaintext?: boolean;
},
): Promise<{
ciphertext: string;
plaintextBase64?: string;
keyId: string;
keyVersion: number;
bytes: number;
}>generateMac
Computes an HMAC of a message with the key's current version and returns it as base64url.
- Permission:
iam:kms:generate-mac, or a grant.
Prop
Type
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/keys/generateMac" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.generateMac(
credential: CredentialInput,
input: { tenantId: string; keyId: string; message?: string; messageBase64?: string },
): Promise<{ mac: string; algorithm: string; keyId: string; keyVersion: number }>get
One key by id or alias: its spec, usage, state, current version, tags, aliases, algorithms and rotation schedule. Never its material.
- Permission:
iam:kms:readoniam/kms/{keyId}, or a grant that allowsread. - Errors:
NOT_FOUNDfor an unknown key or alias in this tenant.
Prop
Type
A KeySummary 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/keys/get" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.get(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeySummary>jwks
Every version's public key of an asymmetric key, as a JWK Set for verifying tokens signed with signJwt.
- Permission:
iam:kms:read.
Prop
Type
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/keys/jwks" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.jwks(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<{ keys: Record<string, unknown>[] }>list
The tenant's keys that the caller may read, newest first, optionally filtered by state or keyUsage.
- Permission:
iam:kms:read, evaluated for each key, so a reader limited by tags sees only matching keys. - Audited as: one
iam:kms:readevent onkms(metadata:listed).
Prop
Type
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/keys/list" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.keys.list(
credential: CredentialInput,
input: {
tenantId: string;
state?: KeyState;
keyUsage?: KeyUsage;
limit?: number;
offset?: number;
},
): Promise<{ keys: KeySummary[]; total: number }>listAliases
Every alias in the tenant, or the aliases of one key (keyId), sorted by name.
- Permission:
iam:kms:readoniam/kms, or on the key whenkeyIdis given.
Prop
Type
An array of AliasSummary.
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/keys/listAliases" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.keys.listAliases(
credential: CredentialInput,
input: { tenantId: string; keyId?: string },
): Promise<AliasSummary[]>listGrants
The key's grants, newest first, each with active (not lapsed).
- Permission:
iam:kms:read.
Prop
Type
An array of GrantSummary.
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/keys/listGrants" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.listGrants(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<GrantSummary[]>listVersions
The key's versions, newest first, with their origin (create, rotate or automatic) and, for asymmetric keys, a fingerprint of the public key.
- Permission:
iam:kms:read.
Prop
Type
An array of KeyVersionSummary.
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/keys/listVersions" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.listVersions(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeyVersionSummary[]>publicKey
The public key of an asymmetric key version (the current one by default), as SPKI PEM and as a JWK whose kid is {keyId}.{version}. Anyone holding it can verify signatures without calling IAM. RSA encryption keys bind a label to their ciphertexts, so values encrypted offline with the public key are not KMS ciphertexts.
- Permission:
iam:kms:read. - Errors:
INVALID_INPUTfor AES and HMAC keys.
Prop
Type
A PublicKeyView 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/keys/publicKey" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.publicKey(
credential: CredentialInput,
input: { tenantId: string; keyId: string; keyVersion?: number },
): Promise<PublicKeyView>reEncrypt
Decrypts a ciphertext and encrypts it again under another key or context, without the plaintext leaving the server.
- Permission:
iam:kms:decrypton the source key andiam:kms:encrypton the destination key. - Audited as: both
iam:kms:decryptandiam:kms:encrypt.
Prop
Type
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/keys/reEncrypt" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"ciphertext": "<ciphertext>",
"destinationKeyId": "<destinationKeyId>"
}'iam.api.keys.reEncrypt(
credential: CredentialInput,
input: {
tenantId: string;
ciphertext: string;
sourceEncryptionContext?: EncryptionContext;
destinationKeyId: string;
destinationEncryptionContext?: EncryptionContext;
},
): Promise<{
ciphertext: string;
keyId: string;
keyVersion: number;
sourceKeyId: string;
sourceKeyVersion: number;
}>retireGrant
Gives up a grant made to the caller's own identity. It needs no permission, so a workload can drop access it no longer uses.
- Audited as:
kms:grant-retire(metadata:grantId). - Errors:
ACCESS_DENIEDfor anyone but the grantee identity.
Prop
Type
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/keys/retireGrant" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"grantId": "<grantId>"
}'iam.api.keys.retireGrant(
credential: CredentialInput,
input: { tenantId: string; grantId: string },
): Promise<{ success: true }>revokeGrant
Revokes a grant.
- Permission:
iam:kms:granton the grant's key.
Prop
Type
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/keys/revokeGrant" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"grantId": "<grantId>"
}'iam.api.keys.revokeGrant(
credential: CredentialInput,
input: { tenantId: string; grantId: string },
): Promise<{ success: true }>rotate
Creates a new version on demand. The new version encrypts, signs and computes MACs from now on, and older versions keep decrypting and verifying.
- Permission:
iam:kms:update. - Audited as:
iam:kms:update(metadata:rotated,keyVersion). - Errors:
KEY_STATE_INVALIDunless the key is enabled;RATE_LIMITEDafter ten on-demand rotations of the key within a day;LIMIT_EXCEEDEDpast 1000 versions.
Prop
Type
A KeySummary 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/keys/rotate" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.rotate(
credential: CredentialInput,
input: { tenantId: string; keyId: string },
): Promise<KeySummary>scheduleDeletion
Schedules the key's destruction after a waiting period of 7 to 30 days (30 by default). The key is unusable while it waits. Afterwards the scheduler job iam.kms.maintain() destroys its material, aliases and grants, and nothing encrypted under it can be decrypted again.
- Permission:
iam:kms:delete, with recent authentication. - Audited as:
iam:kms:delete(metadata:deletionDate); the destruction askms:key-destroy. - Errors:
KEY_STATE_INVALIDwhen the key is already pending deletion;RECENT_AUTH_REQUIRED.
Prop
Type
A KeySummary 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/keys/scheduleDeletion" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.scheduleDeletion(
credential: CredentialInput,
input: { tenantId: string; keyId: string; waitingDays?: number },
): Promise<KeySummary>sign
Signs a message (message or messageBase64, up to 64 KiB) with the key's current version. algorithm defaults to the key's first (ES256, ES384, EdDSA, or PS256 for RSA). ECDSA signatures are DER unless format: 'jose'.
- Permission:
iam:kms:sign, or a grant.resource.algorithmnames the algorithm. - Audited as:
iam:kms:sign(metadata:keyVersion,algorithm).
Prop
Type
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/keys/sign" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.sign(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
message?: string;
messageBase64?: string;
algorithm?: string;
format?: SignatureFormat;
},
): Promise<{
signature: string;
algorithm: string;
format: SignatureFormat;
keyId: string;
keyVersion: number;
}>signJwt
Signs a JWT with a signing key or a MAC key. The header is { alg, kid, typ } with kid = {keyId}.{version} (the same kid as in jwks). iat is added when absent, and exp comes from expiresInSeconds (1 second to 1 year).
Used inKey management
- Permission:
iam:kms:signfor signing keys,iam:kms:generate-macfor MAC keys. Policies also seeresource.jwt(true),resource.jwt.typ,resource.jwt.sub,resource.jwt.issandresource.jwt.aud(orresource.jwt.audiences), so they can limit which tokens a caller mints, or allow tokens but not rawsign. - Audited as: that action (metadata:
keyVersion,algorithm,jwt).
Prop
Type
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/keys/signJwt" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>",
"claims": {}
}'iam.api.keys.signJwt(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
claims: Record<string, unknown>;
algorithm?: string;
expiresInSeconds?: number;
type?: string;
},
): Promise<{ token: string; algorithm: string; keyId: string; keyVersion: number }>update
Changes the description, replaces the tags, or sets or clears (null) the automatic rotation period.
- Permission:
iam:kms:update. When the tags change,iam:kms:updateis evaluated again with the new tags, so a tag-scoped administrator cannot move a key out of their reach or into another team's. New tags must also not give the caller any KMS action on the key that the current tags refuse them. - Audited as:
iam:kms:update(metadata:tags,rotationPeriodDays). - Errors:
ACCESS_DENIEDwhen the new tags would take the key out of the caller's reach or open it to them;KEY_STATE_INVALIDfor a key pending deletion.
Prop
Type
A KeySummary 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/keys/update" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>"
}'iam.api.keys.update(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
description?: string | null;
tags?: Record<string, string>;
rotationPeriodDays?: number | null;
},
): Promise<KeySummary>updateAlias
Points an alias at another key of the same kind and usage, so applications that name the alias switch keys without a deploy.
- Permission:
iam:kms:updateon the new key, on the key the alias named until now, and oniam/kms/alias/{name}. - Audited as:
iam:kms:update(metadata:alias,previousKeyId). - Errors:
INVALID_INPUTwhen the keys differ in kind or usage;NOT_FOUNDfor an unknown alias.
Prop
Type
A AliasSummary 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/keys/updateAlias" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"alias": "<alias>",
"keyId": "<keyId>"
}'iam.api.keys.updateAlias(
credential: CredentialInput,
input: { tenantId: string; alias: string; keyId: string },
): Promise<AliasSummary>verify
Checks a signature from sign with the given keyVersion (the current one by default). Returns { valid }: a mismatch is not an error.
- Permission:
iam:kms:verify, or a grant. - Audited as:
iam:kms:verify(metadata:keyVersion,algorithm,valid).
Prop
Type
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/keys/verify" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>",
"signature": "<signature>"
}'iam.api.keys.verify(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
message?: string;
messageBase64?: string;
signature: string;
algorithm?: string;
keyVersion?: number;
format?: SignatureFormat;
},
): Promise<{ valid: boolean; algorithm: string; keyId: string; keyVersion: number }>verifyJwt
Verifies a JWT made with a key. The kid selects the version, alg must be one of the key's algorithms (so none and algorithm swaps fail), exp and nbf are checked with clockToleranceSeconds (60 by default), and audience and issuer are checked when given. Tokens with a crit header, or an exp or nbf that is not a number, are refused. Returns { valid: true, claims, header } or { valid: false, reason } with reason one of signature, expired, not-yet-valid, audience, issuer, algorithm, header or claims.
Used inKey management
- Permission:
iam:kms:verifyfor signing keys,iam:kms:verify-macfor MAC keys. - Errors:
INVALID_INPUTfor a token that is not a compact JWS, or without a KMSkidand nokeyId.
Prop
Type
One of object | object.
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/keys/verifyJwt" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"token": "<token>"
}'iam.api.keys.verifyJwt(
credential: CredentialInput,
input: {
tenantId: string;
token: string;
keyId?: string;
audience?: string;
issuer?: string;
clockToleranceSeconds?: number;
},
): Promise<JwtVerification>verifyMac
Checks a MAC in constant time with the given keyVersion (the current one by default). Returns { valid }.
- Permission:
iam:kms:verify-mac, or a grant.
Prop
Type
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/keys/verifyMac" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"keyId": "<keyId>",
"mac": "<mac>"
}'iam.api.keys.verifyMac(
credential: CredentialInput,
input: {
tenantId: string;
keyId: string;
message?: string;
messageBase64?: string;
mac: string;
keyVersion?: number;
},
): Promise<{ valid: boolean; keyId: string; keyVersion: number }>Better IAM is created by Sean Filimon
Last updated
invariants
Access invariants are guardrails: statements about who must never, or must always, be able to perform an action on a resource.
ldap
Each organization can publish its directory over LDAP, so applications that only speak LDAP (VPNs, NAS devices, CI servers, wikis, printers) look up people and…