BetterIAM
Server API

compliance

The compliance center: automated checks over the tenant's identity and access state (second factors, password and session rules, inactive accounts, leavers who…

The compliance center: automated checks over the tenant's identity and access state (second factors, password and session rules, inactive accounts, leavers who kept access, stale and long-lived API keys, access reviews, full administrators, owners, separation of duties, audit chain integrity, data-subject request deadlines), mapped to requirements of SOC 2, ISO/IEC 27001:2022, NIST SP 800-53 Rev. 5 and GDPR. A tenant turns checks into controls, evaluates them daily and on demand, accepts findings with time-boxed exceptions that a second person approves, sees each requirement's status, and exports evidence packs signed with an Ed25519 key that auditors verify offline. See the compliance guide; the repository guide is docs/compliance.md.

Evidence, not coverage

Each mapping says a check is evidence for a requirement, not that it covers the requirement. SOC 2 CC6.1 or ISO 27001 8.5 also cover systems, processes and documents outside the identity system, so a passing requirement here passes only for the part its checks measure, and an auditor still assesses the rest.

Checks and statuses

A check returns findings (each with a subject and a detail: identity:ID, session:ID for an API key, trust:ID, role:ID, campaign:ID, request:ID, or tenant:ID:condition for an organization-wide setting), metrics, and a status: pass, fail, warn or not-applicable. The checks are mfa-enforced, mfa-coverage (minimumPercent, 100), password-length (minimum, 12), session-lifetime (maxHours, 168, and idleMinutes, 1440), inactive-accounts (days, 90), leaver-access, stale-api-keys (days, 90), api-key-lifetime (maxDays, 365), access-reviews (intervalDays, 90, minDecidedPercent, 80, and minCoveragePercent, 100), privileged-access (maxHolders, 5), owner-redundancy, separation-of-duties, audit-integrity (verified incrementally from a stored checkpoint; its findings cannot be excepted) and privacy-deadlines; catalog returns each with its parameters' ranges. A check that cannot run fails its control. An approved, unexpired exception removes one finding from its control's status; rawStatus keeps what the check found.

Stored results identify people only by ID. When results are read (listResults, listControls, exportEvidence), findings about people carry name (the email address or name) for callers allowed iam:identities:read on the tenant, and results more than three days old carry stale: true.

Permissions

iam:compliance:read covers catalog, listControls, status, listRuns, listResults, listExceptions, exportEvidence, evidenceKeys and verifyEvidence; iam:compliance:evaluate covers evaluate; iam:compliance:manage covers adoptFramework, the control methods, createException, approveException and revokeException. Actions are checked on iam/TENANT_ID, or on the control (updateControl, deleteControl) or exception (approveException, revokeException) a call acts on. Evaluations read the whole directory whoever triggers them, so results show findings about people the caller may not otherwise read (by ID, unless the caller may read the directory). Results and runs are kept 400 days, and exceptions 400 days after they expire, then iam.sweepExpired() removes them; the scheduler job iam.compliance.evaluateAll() (or the CLI command compliance-evaluate) evaluates every tenant daily.

Methods17
Serveriam.api.compliance
Clientclient.compliance
HTTPPOST /api/iam/compliance/*
MethodWhat it doesAccess
adoptFrameworkAdopts a framework: one control per check its requirements use, mapped to those requirements.Credential
approveExceptionApproves a pending exception, so it covers its finding from the next evaluation until it expires.Credential
catalogReturns the built-in checks with their parameters, and the framework mappings.Credential
createControlAdds a control that runs one check with parameters and mappings of your own.Credential
createExceptionProposes accepting one finding of a control until a date; once a second person approves it, the finding no longer counts toward the control's status.Credential
deleteControlDeletes a control and revokes its exceptions.Credential
evaluateRuns the tenant's enabled controls now, or the ones named in controlKeys.Credential
evidenceKeysReturns the public keys evidence packs are signed with, as a JWKS, for auditors' offline checks.Credential
exportEvidenceReturns a signed evidence pack for auditors: the controls, their latest results, exceptions and recent runs.Credential
listControlsLists the tenant's controls by name, each with its latest result.Credential
listExceptionsLists the tenant's exceptions by expiry, pending, revoked and expired ones included.Credential
listResultsReturns results: the latest of every control, or a control's history, or one run's results.Credential
listRunsLists evaluation runs, newest first.Credential
revokeExceptionEnds an exception, or withdraws a pending one, so its finding counts again from the next evaluation.Credential
statusReturns each framework requirement with the controls that evidence it and their latest status.Credential
updateControlChanges a control's name, description, parameters, mappings, or whether it is evaluated.Credential
verifyEvidenceTells whether an evidence pack is unaltered and was signed by this deployment.Credential

adoptFramework

Adopts a framework: one control per check its requirements use, mapped to those requirements.

POST/api/iam/compliance/adoptFramework
client.compliance.adoptFramework()Credential

Used inCompliance center

  • Permission: iam:compliance:manage on the tenant.
  • Audited as: iam:compliance:manage, plus compliance:framework:adopt with the framework and the control keys.
  • Errors: INVALID_INPUT for a framework other than soc2, iso27001, nist-800-53 or gdpr; LIMIT_EXCEEDED (409) past 200 controls.

Each new control is keyed by the check's id, named and described like the check, uses its default parameters, and is mapped as framework:requirement (soc2:CC6.1). A control already running that check (the one keyed by the check's id, else the first by key) gains the framework's mappings instead, so frameworks share controls. The result is framework and the controls created or extended.

Input

Prop

Type

Returns

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/compliance/adoptFramework" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "framework": "<framework>"
}'
Signature
iam.api.compliance.adoptFramework(
  credential: CredentialInput,
  input: { tenantId: string; framework: string },
): Promise<{ framework: string; controls: ComplianceControl[] }>

approveException

Approves a pending exception, so it covers its finding from the next evaluation until it expires.

POST/api/iam/compliance/approveException
client.compliance.approveException()Credential

Used inCompliance center

  • Permission: iam:compliance:manage on the exception.
  • Audited as: iam:compliance:manage, plus compliance:exception:approve with the control key, subject, author and expiry.
  • Errors: ACCESS_DENIED (403) when the caller proposed the exception or is the subject of its finding; CONFLICT (409) when the exception is not pending or has expired; NOT_FOUND when it is not in this tenant.

The approver must be a second person: exceptions take two holders of iam:compliance:manage. The result is the exception with status: 'approved', approvedBy and approvedAt.

await iam.api.compliance.approveException(credential, { tenantId, exceptionId });
await iam.api.compliance.adoptFramework(admin, { tenantId, framework: 'iso27001' });
Input

Prop

Type

Returns

A ComplianceException 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/compliance/approveException" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "exceptionId": "<exceptionId>"
}'
Signature
iam.api.compliance.approveException(
  credential: CredentialInput,
  input: { tenantId: string; exceptionId: string },
): Promise<ComplianceException>

catalog

Returns the built-in checks with their parameters, and the framework mappings.

POST/api/iam/compliance/catalog
client.compliance.catalog()Credential

Used inCompliance center

  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.

checks lists each check's id, title, description and params (name, description, default, min, max), and noExceptions: true for a check whose findings cannot be excepted (audit-integrity). frameworks lists each framework's id, name and requirements (id, title, and the checks that evidence it). The same data is exported as complianceChecks and complianceFrameworks from @better-iam/server.

Input

Prop

Type

Returns

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/compliance/catalog" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.catalog(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  checks: {
    noExceptions?: true | undefined;
    id: string;
    title: string;
    description: string;
    params: CheckParam[];
  }[];
  frameworks: readonly ComplianceFramework[];
}>

createControl

Adds a control that runs one check with parameters and mappings of your own.

POST/api/iam/compliance/createControl
client.compliance.createControl()Credential

Used inCompliance center

  • Permission: iam:compliance:manage on the tenant.
  • Audited as: iam:compliance:manage, plus compliance:control:create with the key, check and settings.
  • Errors: INVALID_INPUT for a malformed key, an unknown check, a name over 120 characters or a description over 1000, an unknown parameter or a value outside its range, more than 50 mappings or one over 64 characters, or a non-boolean enabled; CONFLICT (409) when a control has the key; LIMIT_EXCEEDED (409) past 200 controls.

key is permanent (1 to 64 lowercase letters, digits, dots, underscores or hyphens, starting with a letter). Parameters left out take their defaults. A mapping in the form framework:requirement counts toward status; other text, such as an internal policy number, is kept for your records and the evidence.

await iam.api.compliance.createControl(admin, {
  tenantId,
  key: 'inactive-30',
  name: 'Accounts unused for 30 days are reviewed',
  checkId: 'inactive-accounts',
  params: { days: 30 },
  mappings: ['internal:POL-7', 'soc2:CC6.2'],
});
Input

Prop

Type

Returns

A ComplianceControl 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/compliance/createControl" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "key": "<key>",
  "name": "<name>",
  "checkId": "<checkId>",
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.createControl(
  credential: CredentialInput,
  input: ControlInput & { tenantId: string },
): Promise<ComplianceControl>

createException

Proposes accepting one finding of a control until a date; once a second person approves it, the finding no longer counts toward the control's status.

POST/api/iam/compliance/createException
client.compliance.createException()Credential

Used inCompliance center

  • Permission: iam:compliance:manage on the tenant.
  • Audited as: iam:compliance:manage, plus compliance:exception with the control key, subject, expiry and status: 'pending'.
  • Errors: NOT_FOUND when the tenant has no control with controlKey; INVALID_INPUT for an expiresAt less than a minute or more than 366 days away, a subject over 300 characters, a reason over 1000, or a control whose check's findings cannot be excepted (audit-integrity); ACCESS_DENIED (403) for a finding about the caller (identity: followed by their own ID).

subject is the finding's subject as the result names it (identity:ID, session:ID, tenant:ID:mfa-optional, ...). The exception starts pending and covers nothing until someone else approves it with approveException; from the next evaluation after that, the finding is marked excepted and the control is judged on the rest. There are no permanent exceptions.

await iam.api.compliance.createException(admin, {
  tenantId,
  controlKey: 'inactive-30',
  subject: `identity:${identityId}`,
  reason: 'On parental leave until December',
  expiresAt: Date.parse('2026-12-31T00:00:00Z'),
});
Input

Prop

Type

Returns

A ComplianceException 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/compliance/createException" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "controlKey": "<controlKey>",
  "subject": "<subject>",
  "reason": "<reason>",
  "expiresAt": 1790000000000
}'
Signature
iam.api.compliance.createException(
  credential: CredentialInput,
  input: {
    tenantId: string;
    controlKey: string;
    subject: string;
    reason: string;
    expiresAt: number;
  },
): Promise<ComplianceException>

deleteControl

Deletes a control and revokes its exceptions.

POST/api/iam/compliance/deleteControl
client.compliance.deleteControl()Credential
  • Permission: iam:compliance:manage on the control.
  • Audited as: iam:compliance:manage, plus compliance:control:delete with the key, check and settings.
  • Errors: NOT_FOUND when the control is not in this tenant.

Its exceptions stay as revoked history, and its past results stay until they expire, 400 days after their evaluation. To stop evaluating a control but keep it, set enabled: false with updateControl.

Input

Prop

Type

Returns

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/compliance/deleteControl" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "controlId": "<controlId>"
}'
Signature
iam.api.compliance.deleteControl(
  credential: CredentialInput,
  input: { tenantId: string; controlId: string },
): Promise<{ deleted: boolean }>

evaluate

Runs the tenant's enabled controls now, or the ones named in controlKeys.

POST/api/iam/compliance/evaluate
client.compliance.evaluate()Credential

Used inCompliance center

  • Permission: iam:compliance:evaluate on the tenant.
  • Audited as: iam:compliance:evaluate, plus compliance:evaluate with the counts and digest, and compliance:control:fail or compliance:control:recover for each control that started or stopped failing.
  • Errors: INVALID_INPUT for more than 100 controlKeys; RATE_LIMITED (429) within a minute of the tenant's last evaluation, scheduled ones included.

The checks read outside any transaction, so an evaluation never holds the store's write lock while it walks a large tenant; the results, the run and their audit events are then written in one short transaction, leaving out a control deleted or changed meanwhile. The result is the run (counts per status and a digest, the SHA-256 of the results' canonical JSON, also recorded in the audit chain) and the results: per control its controlKey, checkId, status, rawStatus (before exceptions), summary, findingsTotal and excepted. The stored results also keep the metrics and up to 200 findings; read them with listResults. Disabled controls and unknown keys are skipped.

const { run, results } = await iam.api.compliance.evaluate(admin, { tenantId });
const failing = results.filter((result) => result.status === 'fail');
Input

Prop

Type

Returns

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/compliance/evaluate" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.evaluate(
  credential: CredentialInput,
  input: { tenantId: string; controlKeys?: string[] },
): Promise<{
  run: ComplianceRun;
  results: {
    controlKey: string;
    checkId: string;
    status: CheckStatus;
    rawStatus: CheckStatus;
    summary: string;
    findingsTotal: number;
    excepted: number;
  }[];
}>

evidenceKeys

Returns the public keys evidence packs are signed with, as a JWKS, for auditors' offline checks.

POST/api/iam/compliance/evidenceKeys
client.compliance.evidenceKeys()Credential

Used inCompliance center

  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.

The result is { keys }, each { kty: 'OKP', crv: 'Ed25519', x, kid, alg: 'EdDSA', use: 'sig' }: first the key derived from the deployment secret, then one per entry of previousSecrets. kid is the key's RFC 7638 thumbprint. Hand the keys to auditors with the pack; verifyEvidencePack(pack, jwks) from @better-iam/server checks a pack with them, without a server.

import { verifyEvidencePack } from '@better-iam/server';

const jwks = await iam.api.compliance.evidenceKeys(credential, { tenantId });
const { valid, reason } = verifyEvidencePack(pack, jwks);
Input

Prop

Type

Returns

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/compliance/evidenceKeys" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.evidenceKeys(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  keys: {
    readonly kty: 'OKP';
    readonly crv: 'Ed25519';
    readonly x: string;
    readonly kid: string;
    readonly alg: 'EdDSA';
    readonly use: 'sig';
  }[];
}>

exportEvidence

Returns a signed evidence pack for auditors: the controls, their latest results, exceptions and recent runs.

POST/api/iam/compliance/exportEvidence
client.compliance.exportEvidence()Credential

Used inCompliance center

  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read, plus compliance:evidence-export with the framework, the number of controls, the pack's digest and the signing key's id.
  • Errors: INVALID_INPUT for an unknown framework.

The pack (format: 'better-iam.compliance-evidence', version: 2) holds the controls (only those mapped to framework when given; disabled ones with enabled: false) with their parameters, mappings and latest result and findings (marked stale past three days), every exception of those controls with its status and approval and revocation history, the 30 latest runs with their digests, and the tenant's audit chain head (auditHead). digest is the SHA-256 (base64url) of its canonical JSON without digest and signature, and signature ({ alg: 'EdDSA', kid, value }) an Ed25519 signature over the digest by a key derived from the deployment secret, published by evidenceKeys and checked by verifyEvidence. Findings name people by ID, and by email address or name when the caller may read the directory, so treat packs as personal data.

Input

Prop

Type

Returns

A EvidencePack 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/compliance/exportEvidence" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.exportEvidence(
  credential: CredentialInput,
  input: { tenantId: string; framework?: string },
): Promise<EvidencePack>

listControls

Lists the tenant's controls by name, each with its latest result.

POST/api/iam/compliance/listControls
client.compliance.listControls()Credential
  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.

Each control carries key, name, description, checkId, params, mappings, enabled, lastStatus, lastEvaluatedAt, and latest, the result of its most recent evaluation, with findings (people's names for callers who may read the directory) and stale (older than three days).

Input

Prop

Type

Returns

An array of 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/compliance/listControls" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.listControls(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  latest: ResultView | undefined;
  key: string;
  name: string;
  description?: string;
  checkId: string;
  params: Record<string, number>;
  mappings: string[];
  enabled: boolean;
  lastStatus?: CheckStatus;
  lastEvaluatedAt?: number;
  lastResultId?: string;
  createdAt: number;
  updatedAt: number;
  id: string;
  tenantId: string;
  uniqueKey?: string;
}[]>

listExceptions

Lists the tenant's exceptions by expiry, pending, revoked and expired ones included.

POST/api/iam/compliance/listExceptions
client.compliance.listExceptions()Credential
  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.

Each exception carries controlKey, subject, reason, expiresAt, createdBy, createdAt, status (pending, approved or revoked; exceptions made before approvals existed count as approved), approvedBy and approvedAt or revokedBy and revokedAt when set, and active (approved and not expired: it covers its finding now). Records are kept until 400 days after they expire.

Input

Prop

Type

Returns

An array of 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/compliance/listExceptions" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.listExceptions(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  status: 'pending' | 'revoked' | 'approved';
  active: boolean;
  controlKey: string;
  subject: string;
  reason: string;
  expiresAt: number;
  createdBy: string;
  createdAt: number;
  approvedBy?: string;
  approvedAt?: number;
  revokedBy?: string;
  revokedAt?: number;
  id: string;
  tenantId: string;
  uniqueKey?: string;
}[]>

listResults

Returns results: the latest of every control, or a control's history, or one run's results.

POST/api/iam/compliance/listResults
client.compliance.listResults()Credential
  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.
  • Errors: INVALID_INPUT for a limit outside 1 to 1000.

Without controlKey or runId, the result is the latest result of each control, by key. With either or both, it is the matching results, newest first, at most limit (100 by default).

Input

Prop

Type

Returns

An array of ResultView.

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/compliance/listResults" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.listResults(
  credential: CredentialInput,
  input: { tenantId: string; controlKey?: string; runId?: string; limit?: number },
): Promise<ResultView[]>

listRuns

Lists evaluation runs, newest first.

POST/api/iam/compliance/listRuns
client.compliance.listRuns()Credential
  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.
  • Errors: INVALID_INPUT for a limit outside 1 to 500.

Each run carries evaluatedAt, counts per status, digest, and triggeredBy (the caller's id, or deployment-operator for the scheduler job). limit is 30 by default.

Input

Prop

Type

Returns

An array of ComplianceRun.

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/compliance/listRuns" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.listRuns(
  credential: CredentialInput,
  input: { tenantId: string; limit?: number },
): Promise<ComplianceRun[]>

revokeException

Ends an exception, or withdraws a pending one, so its finding counts again from the next evaluation.

POST/api/iam/compliance/revokeException
client.compliance.revokeException()Credential
  • Permission: iam:compliance:manage on the exception.
  • Audited as: iam:compliance:manage, plus compliance:exception:revoke with the control key and subject.
  • Errors: NOT_FOUND when the exception is not in this tenant.

The record stays as history, with status: 'revoked', revokedBy and revokedAt, in listExceptions and evidence packs. Revoking an exception that is already revoked changes nothing. The result is { revoked: true }.

Input

Prop

Type

Returns

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/compliance/revokeException" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "exceptionId": "<exceptionId>"
}'
Signature
iam.api.compliance.revokeException(
  credential: CredentialInput,
  input: { tenantId: string; exceptionId: string },
): Promise<{ revoked: boolean }>

status

Returns each framework requirement with the controls that evidence it and their latest status.

POST/api/iam/compliance/status
client.compliance.status()Credential

Used inCompliance center

  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.
  • Errors: INVALID_INPUT for an unknown framework.

Without framework, the result covers every framework a control is mapped to. A requirement's status is no-control without any mapped control, and otherwise the worst of its controls, from worst to best: fail, disabled (a mapped control is disabled), not-evaluated (a mapped control has no result, or only a stale one older than three days), warn, pass, not-applicable. Each control is listed with key, name, enabled, and its latest status, evaluatedAt and stale. total counts the framework's requirements, covered those with a control (enabled or not), and passing those that pass or are not applicable.

const [soc2] = await iam.api.compliance.status(admin, { tenantId, framework: 'soc2' });
const open = soc2.requirements.filter((requirement) => requirement.status === 'fail');
Input

Prop

Type

Returns

An array of FrameworkStatus.

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/compliance/status" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.status(
  credential: CredentialInput,
  input: { tenantId: string; framework?: string },
): Promise<FrameworkStatus[]>

updateControl

Changes a control's name, description, parameters, mappings, or whether it is evaluated.

POST/api/iam/compliance/updateControl
client.compliance.updateControl()Credential
  • Permission: iam:compliance:manage on the control.
  • Audited as: iam:compliance:manage, plus compliance:control:update with the settings before and after.
  • Errors: INVALID_INPUT as for createControl; NOT_FOUND when the control is not in this tenant.

Fields you leave out keep their values; params and mappings replace the whole set. The key and the check cannot change. New parameters apply from the next evaluation.

Input

Prop

Type

Returns

A ComplianceControl 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/compliance/updateControl" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "controlId": "<controlId>"
}'
Signature
iam.api.compliance.updateControl(
  credential: CredentialInput,
  input: Partial<Omit<ControlInput, 'key' | 'checkId'>> & {
    tenantId: string;
    controlId: string;
  },
): Promise<ComplianceControl>

verifyEvidence

Tells whether an evidence pack is unaltered and was signed by this deployment.

POST/api/iam/compliance/verifyEvidence
client.compliance.verifyEvidence()Credential
  • Permission: iam:compliance:read on the tenant.
  • Audited as: iam:compliance:read.
  • Errors: INVALID_INPUT without a pack, or without a digest and signature in its place.

The result is { valid: true } when the pack belongs to this tenant, its digest matches its content, and its signature verifies with the evidence key of the deployment secret or of one of previousSecrets, and { valid: false } for a pack that was edited, belongs to another tenant, or has no signature. For a pack too large to send, pass only its digest and signature: then only the signature is checked, so recompute the digest and check the tenant yourself. Version 1 packs, signed with HMAC-SHA256 before evidence keys existed, still verify here. Auditors without access to the deployment use verifyEvidencePack with the keys from evidenceKeys instead.

const { valid } = await iam.api.compliance.verifyEvidence(admin, { tenantId, pack });
Input

Prop

Type

Returns

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/compliance/verifyEvidence" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.compliance.verifyEvidence(
  credential: CredentialInput,
  input: {
    tenantId: string;
    pack?: EvidencePack;
    digest?: string;
    signature?: EvidencePack['signature'];
  },
): Promise<{ valid: boolean }>

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page