{
 "openapi": "3.1.0",
 "info": {
  "title": "Better IAM HTTP API",
  "version": "0.1.0",
  "description": "The HTTP surface of a Better IAM instance. Every operation is a POST with a JSON body, the `X-Better-IAM: 1` header, and either a bearer token or the session cookie; cookie requests must also send a trusted `Origin`. Responses are `{ \"data\": … }` on success and `{ \"error\": { \"code\", \"message\" } }` on failure."
 },
 "externalDocs": {
  "description": "Better IAM documentation",
  "url": "http://localhost:4000/docs/reference/api"
 },
 "servers": [
  {
   "url": "{origin}/api/iam",
   "description": "Your deployment (the default basePath is /api/iam)",
   "variables": {
    "origin": {
     "default": "https://identity.example.com"
    }
   }
  }
 ],
 "tags": [
  {
   "name": "accessPaths",
   "description": "Access paths tell a denied person what they can do on their own to be allowed, such as stepping up to MFA or\nrequesting a package.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/access-paths"
   }
  },
  {
   "name": "accessRequests",
   "description": "Access requests let members ask for specific roles instead of asking an administrator to bind them by hand.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/access-requests"
   }
  },
  {
   "name": "actions",
   "description": "Actions are the names that roles and policies allow or deny, such as `iam:groups:update` or `documents:write`.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/actions"
   }
  },
  {
   "name": "agents",
   "description": "An agent is an AI agent registered as an account of its own: an identity of kind `agent` that a person answers for.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/agents"
   }
  },
  {
   "name": "agreements",
   "description": "Agreements are versioned terms of use that a tenant asks its members to accept: an acceptable-use policy, an NDA,\ndata-handling rules.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/agreements"
   }
  },
  {
   "name": "analysis",
   "description": "Access analysis scans a tenant's configuration for risky or stale access, such as administrators without MFA or\ndormant accounts that still hold roles.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/analysis"
   }
  },
  {
   "name": "assertions",
   "description": "Assertions are short-lived signed tokens that tell another service who is calling: the caller's identity, tenant,\nroles, groups, and whether they used MFA.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/assertions"
   }
  },
  {
   "name": "audit",
   "description": "The audit log records who did what in a tenant; this group searches it, verifies its tamper-evident hash chain, and\nexports it for archiving.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/audit"
   }
  },
  {
   "name": "auth",
   "description": "The `auth` group signs people in and lets them manage their own account security: passwords, one-time codes, passkeys, MFA, sessions, and recovery.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/auth"
   }
  },
  {
   "name": "authorities",
   "description": "Grant authorities delegate the right to hand out access, with a ceiling on what anything granted under them may\never allow.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/authorities"
   }
  },
  {
   "name": "billing",
   "description": "Billing tells an organization what it spends, on what, and who spent it: usage recorded on meters is priced from a\nrate card per billing account and month, then shared out to the people, agents, teams, departments and projects that\nproduced it.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/billing"
   }
  },
  {
   "name": "bindings",
   "description": "Bindings give a role to a person, service account, or group, and control when that role actually applies.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/bindings"
   }
  },
  {
   "name": "certifications",
   "description": "Certification campaigns turn periodic access reviews into recorded decisions.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/certifications"
   }
  },
  {
   "name": "config",
   "description": "Configuration as code: export a tenant's access model as one JSON document, review changes to it, and apply it\nthe same way to staging and production.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/config"
   }
  },
  {
   "name": "credentials",
   "description": "Credentials manages API keys: opaque bearer tokens that let a [service account](/docs/reference/api/service-accounts)\ncall Better IAM and your product without a person signing in.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/credentials"
   }
  },
  {
   "name": "delegations",
   "description": "A delegation lets one AI agent act for one person, within a scope and for a limited time.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/delegations"
   }
  },
  {
   "name": "departments",
   "description": "Departments are the organization's reporting structure: Engineering, Finance, Sales, and their sub-departments.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/departments"
   }
  },
  {
   "name": "domains",
   "description": "Verified email domains let an organization prove it owns a domain such as `acme.com`, so your sign-in page can find\nthe right tenant from a work email address.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/domains"
   }
  },
  {
   "name": "features",
   "description": "Feature flags turn product features on and off per tenant without a deploy.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/features"
   }
  },
  {
   "name": "groups",
   "description": "Groups let you grant access to many people at once.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/groups"
   }
  },
  {
   "name": "hostnames",
   "description": "Custom hostnames let an organization sign in at an address of its own, such as `login.acme.com`, instead of the\nsubdomain the deployment gives it (`acme.signin.example.com`).",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/hostnames"
   }
  },
  {
   "name": "identities",
   "description": "Identities are the people and service accounts that sign in to a tenant, and this group manages them from\ninvitation to offboarding.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/identities"
   }
  },
  {
   "name": "impact",
   "description": "Impact previews show who would gain or lose which actions before you edit a role or policy or delete a role.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/impact"
   }
  },
  {
   "name": "inference",
   "description": "Decides which people, service accounts, and AI agents may call which AI models, caps what they spend, and meters every\ncall.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/inference"
   }
  },
  {
   "name": "invariants",
   "description": "Access invariants are guardrails: statements about who must never, or must always, be able to perform an action on\na resource.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/invariants"
   }
  },
  {
   "name": "links",
   "description": "Account links connect a person's separate accounts in different tenants so your app can offer an account switcher.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/links"
   }
  },
  {
   "name": "oidcProviders",
   "description": "OIDC providers are the external token issuers a tenant trusts for web-identity federation: GitHub Actions, GitLab,\na Kubernetes cluster, or a cloud workload identity service.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/oidc-providers"
   }
  },
  {
   "name": "onboarding",
   "description": "Onboarding flows are checklists for newcomers, customized at every level of the tenant tree.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/onboarding"
   }
  },
  {
   "name": "packages",
   "description": "Access packages bundle roles and group memberships that are granted, requested, and removed together.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/packages"
   }
  },
  {
   "name": "policies",
   "description": "Policies are named, versioned policy documents that roles attach to grant access.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/policies"
   }
  },
  {
   "name": "relationships",
   "description": "Relationships record that a person or group stands in a named relation, such as `owner` or `viewer`, to one resource.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/relationships"
   }
  },
  {
   "name": "reports",
   "description": "The access report gathers a tenant's access-lifecycle state in one document: what is about to end, who is elevated\nright now, and which API keys nobody uses.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/reports"
   }
  },
  {
   "name": "resourceTypes",
   "description": "Resource types let a tenant describe its own kinds of resources at runtime, with their actions, typed attributes,\nrelations, and parent.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/resource-types"
   }
  },
  {
   "name": "resources",
   "description": "This group registers the resources your product protects, so authorization can decide about them without calling\nback into your application.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/resources"
   }
  },
  {
   "name": "roleMining",
   "description": "Role mining reads who holds which roles today and suggests simpler, narrower ways to grant the same access.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/role-mining"
   }
  },
  {
   "name": "roles",
   "description": "Roles are named sets of permissions for a job function, such as Editor or Approver, that bindings give to people\nand groups.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/roles"
   }
  },
  {
   "name": "root",
   "description": "Root administrators run the platform itself: they create organizations, set plan limits, and help customers who are\nlocked out.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/root"
   }
  },
  {
   "name": "security",
   "description": "Network blocks shut out an IP address or range during an incident, for one tenant or, set by root administrators,\nfor the whole installation.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/security"
   }
  },
  {
   "name": "serviceAccounts",
   "description": "Service accounts are identities for machines: a deploy pipeline, a billing worker, a partner integration.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/service-accounts"
   }
  },
  {
   "name": "sod",
   "description": "Separation-of-duties rules name roles that nobody may hold together, such as creating suppliers and approving\npayments to them.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/sod"
   }
  },
  {
   "name": "sts",
   "description": "The `sts` group issues and inspects temporary credentials, the way a cloud security token service does.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/sts"
   }
  },
  {
   "name": "teams",
   "description": "Teams are the working units inside an organization: Platform, Site Reliability, the Payments squad.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/teams"
   }
  },
  {
   "name": "tenants",
   "description": "Tenants are the isolated organizations, projects, and other units your platform serves, arranged in a tree under\none root tenant.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/tenants"
   }
  },
  {
   "name": "trust",
   "description": "A trust lets one named identity, usually from another tenant, temporarily assume a role in this tenant.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/trust"
   }
  },
  {
   "name": "webhooks",
   "description": "Webhooks push a tenant's audit events to an HTTPS endpoint you run, signed so the endpoint can tell they came from\nBetter IAM.",
   "externalDocs": {
    "url": "http://localhost:4000/docs/reference/api/webhooks"
   }
  }
 ],
 "paths": {
  "/accessPaths/find": {
   "post": {
    "operationId": "accessPaths.find",
    "tags": [
     "accessPaths"
    ],
    "summary": "Lists what you could do yourself to be allowed an action on a resource you are denied.",
    "description": "- **Permission:** None beyond your own ordinary session of the tenant (not an assumed role, another tenant's\n  session, or impersonation).\n- **Audited as:** Not audited; nothing is saved.\n- **Errors:** `ACCESS_DENIED` from a role session or another tenant's session; `IMPERSONATION_RESTRICTED` from an\n  impersonation session; `INVALID_ACTION` for an action the catalog does not know; `INVALID_INPUT` when the action or\n  resource is missing.\n\nWhen you are already allowed, the result is `allowed: true` with the decision reason and no paths. When you are\ndenied, `reason` is always `ACCESS_DENIED`: like `authorize`, the call does not reveal which statement refused you.\nCall it from the code path that handles a denial, with the same action and resource you just checked.\n`useAccessPaths` wraps it for React and Vue apps.\n\n```ts\nconst result = await iam.api.accessPaths.find(credential, {\n  tenantId,\n  action: 'documents:delete',\n  resource: { type: 'document', id: 'doc_42' },\n});\nfor (const path of result.paths) {\n  if (path.kind === 'mfa') showStepUpButton();\n  if (path.kind === 'activate') showActivateButton(path.bindingId, path.role.name);\n  if (path.kind === 'request-package') showRequestButton(path.package.id, path.requireJustification);\n  if (path.kind === 'accept-agreements') showTermsDialog(path.agreements);\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-paths#find"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "action": {
          "type": "string"
         },
         "resource": {
          "type": "object",
          "properties": {
           "type": {
            "type": "string"
           },
           "id": {
            "type": "string"
           }
          },
          "required": [
           "type",
           "id"
          ],
          "additionalProperties": false
         }
        },
        "required": [
         "tenantId",
         "action",
         "resource"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessPathsResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/approve": {
   "post": {
    "operationId": "accessRequests.approve",
    "tags": [
     "accessRequests"
    ],
    "summary": "Approves a pending request, binding each requested role to the requester under your own grant authority.",
    "description": "- **Permission:** `iam:access-requests:review` on the request, plus `iam:bindings:create` on each requested role\n  and a grant authority, exactly as [`bindings.create`](/docs/reference/api/bindings#create) requires.\n- **Audited as:** `iam:access-requests:review`, plus `access-request:approve` with the requester, roles, binding IDs,\n  and `grantExpiresAt`.\n- **Errors:** `INVALID_TRANSITION` (409) when the request is no longer pending, including when it has lapsed;\n  `ACCESS_DENIED` when you are the requester or cannot bind one of the roles; `INVALID_IDENTITY` when the requester\n  is not active; `NOT_FOUND` when the request, the requester, or a role is gone; `INVALID_INPUT` for a\n  `durationSeconds` out of range; `GRANT_AUTHORITY_REQUIRED` without a grant authority; `SOD_CONFLICT` when the roles\n  would create a [separation-of-duties](/docs/guides/authorization/separation-of-duties) conflict;\n  `INVARIANT_VIOLATION` when they would break an enforced invariant.\n\n`durationSeconds` overrides the duration the requester asked for; the bindings end that long after approval. If the\nrequester already holds one of the roles through a binding under your authority, that binding is reused and its end\nreplaced by the approved one (removed, when no duration applies). The optional `note` is stored on the request.\n\n```ts\nawait iam.api.accessRequests.approve(reviewerCredential, {\n  tenantId,\n  requestId,\n  durationSeconds: 8 * 60 * 60, // one working day instead of the week they asked for\n  note: 'Approved for the incident review.',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#approve"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "durationSeconds": {
          "type": "number"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessRequest"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/cancel": {
   "post": {
    "operationId": "accessRequests.cancel",
    "tags": [
     "accessRequests"
    ],
    "summary": "Withdraws one of your own pending requests.",
    "description": "- **Permission:** `iam:access-requests:create` on the request, and you must be the requester.\n- **Audited as:** `iam:access-requests:create`.\n- **Errors:** `ACCESS_DENIED` when the request is someone else's; `INVALID_TRANSITION` when it is no longer pending;\n  `NOT_FOUND` when it is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#cancel"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "status": {
             "type": "string"
            },
            "reviewedAt": {
             "type": "number"
            },
            "requesterId": {
             "type": "string"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "justification": {
             "type": "string"
            },
            "durationSeconds": {
             "type": "number"
            },
            "createdAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "reviewerId": {
             "type": "string"
            },
            "note": {
             "type": "string"
            },
            "bindingIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "grantExpiresAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "status",
            "reviewedAt",
            "requesterId",
            "roleIds",
            "createdAt",
            "expiresAt",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/create": {
   "post": {
    "operationId": "accessRequests.create",
    "tags": [
     "accessRequests"
    ],
    "summary": "Asks for one or more roles for yourself, optionally for a limited time.",
    "description": "- **Permission:** `iam:access-requests:create` on the tenant, from an ordinary session of that tenant.\n- **Audited as:** `iam:access-requests:create`.\n- **Errors:** `INVALID_INPUT` from a role session or another tenant's session, for zero or more than 20 roles, or a\n  `durationSeconds` out of range; `PROTECTED_RESOURCE` for an owner role; `NOT_FOUND` when a role is not in this\n  tenant; `CONFLICT` when a pending request for the same set of roles exists; `TOO_MANY_REQUESTS` (429) when you\n  already have 20 pending requests; `TENANT_INACTIVE` when the tenant is not active.\n\nNothing is granted until a reviewer approves. `justification` (up to 2048 characters) is shown to reviewers. Grant\n`iam:access-requests:create` to every member, for example through a group everyone belongs to, and\n`iam:access-requests:review` to the people who decide.\n\n```ts\nconst request = await iam.api.accessRequests.create(memberCredential, {\n  tenantId,\n  roleIds: [supportAdminRole.id],\n  justification: 'Covering the support rotation this week',\n  durationSeconds: 7 * 24 * 60 * 60,\n});\n// request.status === 'pending'; request.expiresAt is when it lapses if nobody decides\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "justification": {
          "type": "string"
         },
         "durationSeconds": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "roleIds"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessRequest"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/deny": {
   "post": {
    "operationId": "accessRequests.deny",
    "tags": [
     "accessRequests"
    ],
    "summary": "Refuses a pending request, with an optional note for the requester.",
    "description": "- **Permission:** `iam:access-requests:review` on the request.\n- **Audited as:** `iam:access-requests:review`, plus `access-request:deny` with the requester and roles.\n- **Errors:** `INVALID_TRANSITION` when the request is no longer pending; `NOT_FOUND` when it is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#deny"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessRequest"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/get": {
   "post": {
    "operationId": "accessRequests.get",
    "tags": [
     "accessRequests"
    ],
    "summary": "Returns one request.",
    "description": "- **Permission:** `iam:access-requests:read` on the request.\n- **Audited as:** `iam:access-requests:read`.\n- **Errors:** `NOT_FOUND` when the request is not in this tenant.\n\nA pending request past its lifetime is returned as `expired`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessRequest"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/list": {
   "post": {
    "operationId": "accessRequests.list",
    "tags": [
     "accessRequests"
    ],
    "summary": "Lists the tenant's requests, newest first, optionally by status or requester.",
    "description": "- **Permission:** `iam:access-requests:read` on the tenant.\n- **Audited as:** `iam:access-requests:read`.\n- **Errors:** `INVALID_INPUT` for an unknown `status`.\n\nThe `status` filter matches the stored status, so until the purge worker runs, `status: 'pending'` can include\nlapsed requests, which are reported as `expired`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "approved",
           "denied",
           "cancelled",
           "expired"
          ]
         },
         "requesterId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AccessRequest"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/accessRequests/listMine": {
   "post": {
    "operationId": "accessRequests.listMine",
    "tags": [
     "accessRequests"
    ],
    "summary": "Lists your own requests, newest first, optionally by status.",
    "description": "- **Permission:** `iam:access-requests:create` on the tenant, so anyone who may ask can see their own requests.\n- **Audited as:** `iam:access-requests:create`.\n- **Errors:** `INVALID_INPUT` for an unknown `status`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/access-requests#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "approved",
           "denied",
           "cancelled",
           "expired"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AccessRequest"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/actions/list": {
   "post": {
    "operationId": "actions.list",
    "tags": [
     "actions"
    ],
    "summary": "Lists every action the tenant can use in policies: platform actions first, then the tenant's own.",
    "description": "- **Permission:** `iam:actions:read` on the tenant.\n- **Audited as:** `iam:actions:read`.\n\nEach entry has a `name`, a `source` of `platform` or `tenant`, and the `resourceType` it belongs to when it was\ndeclared under one. Tenant actions also carry their `description`. Use it to populate policy and role editors.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/actions#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ActionSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/actions/register": {
   "post": {
    "operationId": "actions.register",
    "tags": [
     "actions"
    ],
    "summary": "Adds a {type}:{verb} action under one of the tenant's resource types.",
    "description": "- **Permission:** `iam:actions:create` on the tenant.\n- **Audited as:** `iam:actions:create`.\n- **Errors:** `CATALOG_LOCKED` (403) when the deployment does not allow tenant-defined actions; `INVALID_ACTION` when\n  the name is not `{type}:{verb}`, collides with a platform action or namespace, or its type is not a tenant-defined\n  resource type; `CONFLICT` when the action already exists.\n\nThe verb starts with a letter and uses letters, digits, `_`, or `-`. The description is optional, at most 512\ncharacters.\n\n```ts\nawait iam.api.actions.register(credential, {\n  tenantId,\n  name: 'contract:countersign',\n  description: 'Countersign a contract after legal review',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/actions#register"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ActionDefinition"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/actions/unregister": {
   "post": {
    "operationId": "actions.unregister",
    "tags": [
     "actions"
    ],
    "summary": "Removes a tenant-defined action from the catalog.",
    "description": "- **Permission:** `iam:actions:delete` on the tenant.\n- **Audited as:** `iam:actions:delete`.\n- **Errors:** `NOT_FOUND` when the tenant has no action by that name (platform actions included);\n  `RESOURCE_IN_USE` while a stored policy or inline role document names the action exactly.\n\nRemove the action from every policy and role first; the check exists so no stored document is left naming an action\nthat no longer exists. Wildcard patterns such as `contract:*` do not count as references.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/actions#unregister"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/activity": {
   "post": {
    "operationId": "agents.activity",
    "tags": [
     "agents"
    ],
    "summary": "Returns what an agent did, newest first: the audit events of its own credentials and of the sessions in which it acted for people.",
    "description": "- **Permission:** `iam:agents:read` on the agent, or none for its sponsor in their own session.\n- **Audited as:** `iam:agents:read` for administrators; not audited for the sponsor.\n- **Errors:** `NOT_FOUND` when the id is not an agent of this tenant; `INVALID_INPUT` for a `limit` outside 1 to 500 or\n  a malformed `offset`, `from`, or `to`.\n\nAn event belongs to the agent when the agent is its actor (its API keys and session tokens) or when its\n`sessionContext.agentId` names the agent (a delegated session, where the actor is the person it acted for). Both\nallowed and denied events are included, so a sponsor sees what the agent tried as well as what it did. Page with\n`limit` (100 by default) and `offset`, and bound the time with `from` and `to` (epoch milliseconds).\n\n```ts\nconst recent = await iam.api.agents.activity(sponsorSession, { tenantId, agentId, limit: 50 });\nconst refused = recent.filter((event) => event.outcome === 'deny');\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#activity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/ActivityQuery"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "agentId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "agentId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AuditEvent"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/catalog": {
   "post": {
    "operationId": "agents.catalog",
    "tags": [
     "agents"
    ],
    "summary": "Lists the agents people of the tenant may delegate to, with what a person needs to decide.",
    "description": "- **Permission:** None beyond a person's own session of the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for any other credential, including a session of another tenant.\n\nOnly active, delegable agents in good standing (`ok`) are listed, sorted by name. Each entry has `id` and `name`, the\n`description`, `purpose`, `model`, `provider`, `url`, `protocols`, and `tokenAudiences` when set, and `sponsorName`, so\na consent screen can show who answers for the agent and which outside services it may carry a delegation to. Keys, the boundary, and sponsor ids are not included. Use it for a \"connect an\nagent\" page that ends in [`delegations.grant`](/docs/reference/api/delegations#grant).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#catalog"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AgentListing"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/create": {
   "post": {
    "operationId": "agents.create",
    "tags": [
     "agents"
    ],
    "summary": "Registers an AI agent with a sponsor and an optional profile.",
    "description": "- **Permission:** `iam:agents:create` on the tenant.\n- **Audited as:** `iam:agents:create`, plus `agent:create` with the sponsor, model, and provider.\n- **Errors:** `INVALID_SPONSOR` when `sponsorId` is not an active, unexpired person of the tenant, or when it is left\n  out and the caller is not a person in their own session (an API key, for example); `LIMIT_EXCEEDED` (409) past the\n  tenant's `agents` limit; `INVALID_INPUT` for a malformed profile field, a `description` over 512 characters, or an\n  `expiresAt` that is not in the next ten years; `INVALID_POLICY` or `INVALID_ACTION` for a malformed `boundary`.\n\nThe agent starts `active` with no keys: issue them with `credentials.create` as for a service account, and give it\naccess with roles and bindings. `expiresAt` (epoch milliseconds) schedules its deactivation. Deleted agents do not\ncount toward the limit. The result carries the agent's `standing` and a `sponsor` summary (id, name, email, status).\n\n```ts\nconst agent = await iam.api.agents.create(credential, {\n  tenantId,\n  name: 'Support triage',\n  purpose: 'Labels and routes incoming support tickets',\n  model: 'claude-sonnet-5',\n  provider: 'anthropic',\n  protocols: ['mcp'],\n  sponsorId: aliceId,\n  boundary: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['tickets:*'], resources: ['ticket/*'] }],\n  },\n});\nconst { token } = await iam.api.credentials.create(credential, {\n  tenantId,\n  identityId: agent.id,\n  name: 'production',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreateAgentInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgentSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/delete": {
   "post": {
    "operationId": "agents.delete",
    "tags": [
     "agents"
    ],
    "summary": "Deletes an agent, ending its keys and sessions and revoking every delegation to it.",
    "description": "- **Permission:** `iam:agents:delete` on the agent, and a recent sign-in.\n- **Audited as:** `iam:agents:delete`, plus `identity:delete` with `kind: 'agent'` and `delegationsRevoked`.\n- **Errors:** `RECENT_AUTH_REQUIRED` without a recent sign-in (temporary credentials never have one); `NOT_FOUND` when\n  the id is not an agent of this tenant; `CONFLICT` when it is already deleted.\n\nPending requests and active delegations involving the agent become `revoked`, and their delegated sessions end at once.\nLike any deleted identity, the agent leaves a tombstone (status `deleted`) so audit records keep resolving it, and its\nbindings, group memberships, and relationships are removed. The result is the tombstone. To stop an agent for a while\nand keep its keys, use `suspend`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/directory": {
   "post": {
    "operationId": "agents.directory",
    "tags": [
     "agents"
    ],
    "summary": "Lists the tenant's agents that hold a current attested A2A card, for finding an agent to work with or hand work to.",
    "description": "- **Permission:** None beyond a credential of the tenant: a person's session, an agent's or service account's key, or\n  a delegated session.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for a credential of another tenant.\n\nEvery [`signCard`](#signcard) stores the agent's latest signed card as its directory entry. Entries are listed while\ntheir attestation is valid and the agent is in good standing, sorted by name: `{ agentId, name, card, attestation,\nexpiresAt }`, where `card` is the signed card itself, ready for `verifyAgentCard` from `@better-iam/a2a`. `skill`\nkeeps agents whose card offers a skill with that id or tag, and `protocol` agents whose attestation lists that\nprotocol. Suspending or deleting an agent removes it; an expired entry is swept away.\n\n```ts\nconst [translator] = await iam.api.agents.directory(credential, { tenantId, skill: 'translate' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#directory"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "skill": {
          "type": "string"
         },
         "protocol": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AgentDirectoryEntry"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/get": {
   "post": {
    "operationId": "agents.get",
    "tags": [
     "agents"
    ],
    "summary": "Returns one agent with its standing, its live API keys, and counts of its delegations.",
    "description": "- **Permission:** `iam:agents:read` on the agent, or none for the agent's sponsor in their own session.\n- **Audited as:** `iam:agents:read` for administrators; not audited for the sponsor.\n- **Errors:** `NOT_FOUND` when the id is not an agent of this tenant.\n\n`keys` lists unexpired API keys by label only (id, name, creation and expiry times, and `lastUsedAt` once used), never\ntoken material. `delegations` counts the `active` and `pending` ones that have not lapsed, and\n`liveDelegatedSessions` the unexpired delegated sessions. A deleted agent is still returned, with standing `deleted`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgentDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/list": {
   "post": {
    "operationId": "agents.list",
    "tags": [
     "agents"
    ],
    "summary": "Lists the tenant's agents, newest first, optionally only one sponsor's or those in one standing.",
    "description": "- **Permission:** `iam:agents:read` on the tenant.\n- **Audited as:** `iam:agents:read`.\n\nDeleted agents are left out unless `includeDeleted: true`. `standing: 'sponsor-inactive'` finds the agents that\nstopped because their sponsor left, the ones to hand to someone else with `update`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "sponsorId": {
          "type": "string"
         },
         "standing": {
          "type": "string",
          "enum": [
           "expired",
           "deleted",
           "ok",
           "suspended",
           "sponsor-missing",
           "sponsor-inactive"
          ]
         },
         "includeDeleted": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AgentSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/listMine": {
   "post": {
    "operationId": "agents.listMine",
    "tags": [
     "agents"
    ],
    "summary": "Returns the agents you sponsor, newest first, with the same detail as get.",
    "description": "- **Permission:** None beyond a person's own session of the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for any other credential, including a session of another tenant.\n\nDeleted agents are left out. It backs a \"my agents\" page where sponsors see keys and delegations and reach the kill\nswitch.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AgentDetail"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/resume": {
   "post": {
    "operationId": "agents.resume",
    "tags": [
     "agents"
    ],
    "summary": "Lifts an agent's suspension, so its kept API keys work again.",
    "description": "- **Permission:** `iam:agents:update` on the agent and a recent sign-in, or none for the sponsor when they made the\n  suspension themselves.\n- **Audited as:** `agent:resume`; an administrator's call also as `iam:agents:update`.\n- **Errors:** `ACCESS_DENIED` when the sponsor tries to lift someone else's suspension; `RECENT_AUTH_REQUIRED` for an\n  administrator without a recent sign-in; `CONFLICT` (409) when the agent is not suspended; `INVALID_TRANSITION` (409)\n  when it has expired (extend or clear `expiresAt` with `update` first); `NOT_FOUND` for a deleted agent.\n\nThe agent returns to `active` with its keys, roles, and delegations as they were. Delegated sessions and session\ntokens ended by the suspension do not come back; the agent opens new ones. Its standing stays other than `ok` while its\nsponsor is inactive.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#resume"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgentSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/signCard": {
   "post": {
    "operationId": "agents.signCard",
    "tags": [
     "agents"
    ],
    "summary": "Signs an agent's A2A (Agent2Agent) agent card, so other agents can check that it is a registered agent in good standing of your organization.",
    "description": "- **Permission:** None for the agent itself with its own unscoped API key (not a key with `scopes`, and not a session\n  token, role or delegated session); none for its sponsor in their own session; otherwise `iam:agents:update` on the\n  agent.\n- **Audited as:** `agent:card-sign`, with the card's `url` and the attestation's expiry; an administrator's call also as\n  `iam:agents:update`.\n- **Errors:** `FEATURE_DISABLED` (403) without the `a2a` option; `INVALID_INPUT` when the agent has no registered `url`,\n  when the card's `url`, an `additionalInterfaces[].url` or a `supportedInterfaces[].url` is not on that URL's origin,\n  or when the card is not a JSON object with a `name` of 1 to 256 characters (or is larger than 64 KiB);\n  `INVALID_IDENTITY` (409) when the agent is not in good standing; `NOT_FOUND` when the id is not an agent of this\n  tenant; `ACCESS_DENIED` for anyone else, including another agent's key or a scoped key of the agent itself.\n\nPass the card as `card`. IAM drops any `signatures` and any attestation already in the card and sets `provider` to the\ntenant's name at the agent's registered origin, whatever the card said. It then adds the extension\n`urn:better-iam:a2a:attestation:v1` to `capabilities.extensions`. Its params are `issuer`,\n`tenantId`, `organization`, `agentId`, `agentName`, `sponsored: true`, `delegable`, `model`, `provider`, `protocols`,\nand `issuedAt`/`expiresAt` (ISO 8601, `a2a.cardLifetimeSeconds` apart, one hour by default). IAM signs the RFC 8785\ncanonical form of the card (without `signatures`) as a detached JWS: `alg` EdDSA or ES256, the key's `kid`, `typ`\n`JOSE`, and `jku` when `a2a.jwksUrl` is set. The result is `{ card, attestation, expiresAt }`. `card` carries one\nentry in `signatures`, and `expiresAt` is in epoch milliseconds.\n\nVerifiers check the card against the deployment's public card keys (`iam.a2a.jwks()`, served at `a2a.jwksUrl`) with\n`verifyAgentCard` or `discoverAgent` from `@better-iam/a2a`. Suspending, deleting, or un-sponsoring the agent stops new\nsignatures at once, and signed cards stop verifying when their attestation expires. An agent's A2A server usually\nre-signs its own card with `createCardAttestor`.\n\n```ts\nconst { card } = await iam.api.agents.signCard(\n  { token: agentKey },\n  { tenantId, agentId, card: { name: 'Triage', url: 'https://triage.acme.test/a2a', skills: [] } },\n);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#signcard"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         },
         "card": {
          "type": "object",
          "properties": {},
          "additionalProperties": {}
         }
        },
        "required": [
         "tenantId",
         "agentId",
         "card"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SignedAgentCard"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/standing": {
   "post": {
    "operationId": "agents.standing",
    "tags": [
     "agents"
    ],
    "summary": "Tells whether an agent may act right now and, if not, why.",
    "description": "- **Permission:** `iam:agents:read` on the agent, or none for its sponsor in their own session.\n- **Audited as:** `iam:agents:read` for administrators; not audited for the sponsor.\n- **Errors:** `NOT_FOUND` when the id is not an agent of this tenant.\n\nThe result is `{ agentId, standing }` with one of the values under [Sponsors and standing](#sponsors-and-standing), a\nlighter call than `get` when only the answer matters.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#standing"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "agentId": {
             "type": "string"
            },
            "standing": {
             "$ref": "#/components/schemas/AgentStanding"
            }
           },
           "required": [
            "agentId",
            "standing"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/suspend": {
   "post": {
    "operationId": "agents.suspend",
    "tags": [
     "agents"
    ],
    "summary": "Stops an agent at once (the kill switch) and keeps its API keys for resume.",
    "description": "- **Permission:** `iam:agents:update` on the agent, or none for its sponsor in their own session.\n- **Audited as:** `agent:suspend`, with the `reason` and how many delegated sessions and session tokens ended; an\n  administrator's call also as `iam:agents:update`.\n- **Errors:** `CONFLICT` (409) when it is already suspended; `NOT_FOUND` for a deleted agent or an id that is not an\n  agent of this tenant; `ACCESS_DENIED` for anyone else.\n\nThe agent becomes `disabled`, and `agent.suspended` records who suspended it, when, and why (`reason`, up to 512\ncharacters). Its API keys are refused while it is suspended, and its live delegated sessions and session tokens are\ndeleted now, so an agent working for people stops mid-task. Delegations stay in place for after `resume`. No recent\nsign-in is needed, so a sponsor can react immediately.\n\n```ts\nawait iam.api.agents.suspend(aliceSession, { tenantId, agentId, reason: 'Looping on the wiki' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#suspend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgentSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/suspendAll": {
   "post": {
    "operationId": "agents.suspendAll",
    "tags": [
     "agents"
    ],
    "summary": "The organization-wide emergency stop: suspends every active agent of the tenant at once.",
    "description": "- **Permission:** `iam:agents:update` on the tenant; no recent sign-in, so it works during an incident.\n- **Audited as:** `agent:suspend-all` with the `reason`, the filters, and how many agents stopped; `agent:suspend` for\n  each agent; and `iam:agents:update`.\n- **Errors:** `INVALID_INPUT` without a `reason` (up to 512 characters); `ACCESS_DENIED` without the permission.\n\nEach active agent is suspended exactly as [`suspend`](#suspend) does: its credentials are refused and its live\ndelegated sessions and session tokens end now. `sponsorId`, `provider`, and `model` narrow the stop to one sponsor's\nagents or those running on one provider or model. Agents already suspended are left as they are. The result is\n`{ suspended, agentIds }`; agents come back one at a time with [`resume`](#resume).\n\n```ts\nawait iam.api.agents.suspendAll(admin, { tenantId, reason: 'Prompt injection incident' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#suspendall"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         },
         "sponsorId": {
          "type": "string"
         },
         "provider": {
          "type": "string"
         },
         "model": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "suspended": {
             "type": "number"
            },
            "agentIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            }
           },
           "required": [
            "suspended",
            "agentIds"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agents/update": {
   "post": {
    "operationId": "agents.update",
    "tags": [
     "agents"
    ],
    "summary": "Changes an agent's name, description, expiry, attributes, profile, or sponsor.",
    "description": "- **Permission:** `iam:agents:update` on the agent.\n- **Audited as:** `iam:agents:update`, plus `agent:sponsor-change` (with `from` and `to`) when the sponsor changes.\n- **Errors:** `INVALID_SPONSOR` when the new `sponsorId` is not an active person of the tenant; `NOT_FOUND` for a\n  deleted agent or an id that is not an agent of this tenant; `INVALID_INPUT`, `INVALID_POLICY`, or `INVALID_ACTION`\n  as for `create`.\n\nFields you leave out keep their values; `null` clears `description`, `expiresAt`, and the optional profile fields, and\n`attributes` replaces the whole set. A new `boundary` and `delegable: false` apply to live sessions at once, because\nboth are read on every decision. Naming a new sponsor is how you bring back an agent whose sponsor left.\n\n```ts\n// The sponsor left without a successor: hand the agent to someone else.\nawait iam.api.agents.update(credential, { tenantId, agentId, sponsorId: bobId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agents#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/UpdateAgentInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgentSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/accept": {
   "post": {
    "operationId": "agreements.accept",
    "tags": [
     "agreements"
    ],
    "summary": "Records that you accept the given version of an agreement.",
    "description": "- **Permission:** None beyond an ordinary session of the agreement's tenant.\n- **Audited as:** `agreement:accept`, with the agreement's name and version.\n- **Errors:** `VERSION_CONFLICT` (409) when `version` is not the current version; `IMPERSONATION_RESTRICTED` from an\n  impersonation session; `INVALID_INPUT` for a service account; `ACCESS_DENIED` from a role session or another\n  tenant's session; `NOT_FOUND` when the agreement is not in this tenant.\n\nPass the `version` you showed the person, from `listMine`. If the agreement changed in the meantime the call fails,\nso nobody accepts text they were not shown. Accepting again restarts the `reacceptAfterDays` clock. The acceptance\napplies from the next authorization check; enforced invariants do not guard it.\n\n```ts\nconst mine = await iam.api.agreements.listMine(credential, { tenantId });\nconst owed = mine.filter((agreement) => agreement.required && !agreement.accepted);\nfor (const agreement of owed)\n  await iam.api.agreements.accept(credential, { tenantId, agreementId: agreement.id, version: agreement.version });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#accept"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agreementId": {
          "type": "string"
         },
         "version": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "agreementId",
         "version"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "accepted": {
             "type": "boolean"
            },
            "version": {
             "type": "number"
            },
            "acceptedAt": {
             "type": "number"
            }
           },
           "required": [
            "accepted",
            "version",
            "acceptedAt"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/create": {
   "post": {
    "operationId": "agreements.create",
    "tags": [
     "agreements"
    ],
    "summary": "Publishes a new agreement at version 1, required by default.",
    "description": "- **Permission:** `iam:agreements:manage` on the tenant.\n- **Audited as:** `iam:agreements:manage`.\n- **Errors:** `CONFLICT` when an agreement with the same name (ignoring case) exists; `LIMIT_EXCEEDED` (409) when\n  the tenant already has 50; `INVALID_INPUT` for an empty name or one over 100 characters, empty content or content\n  over 50 000 characters or with control characters other than tabs and line breaks, a `url` that is not http(s), or\n  a `reacceptAfterDays` outside 1 to 3650; `INVARIANT_VIOLATION` when a new required agreement would make a policy\n  deny someone an enforced invariant says must be allowed.\n\n`content` is the text people accept (plain text or Markdown); `url` optionally links to the canonical document.\n`required: false` makes it optional: it never counts toward `principal.pendingAgreements`, and people who accept it\nappear in `principal.agreements`. Publishing a required agreement raises every person's\n`principal.pendingAgreements` at once: if a policy already denies on that count, people lose the access it covers\nuntil they accept.\n\n```ts\nawait iam.api.agreements.create(credential, {\n  tenantId,\n  name: 'Acceptable use',\n  content: 'Use company systems for work. Report incidents within 24 hours.',\n  url: 'https://intranet.example.com/policies/acceptable-use',\n  reacceptAfterDays: 365,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/AgreementInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Agreement"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/delete": {
   "post": {
    "operationId": "agreements.delete",
    "tags": [
     "agreements"
    ],
    "summary": "Deletes an agreement together with every acceptance of it.",
    "description": "- **Permission:** `iam:agreements:manage` on the agreement.\n- **Audited as:** `iam:agreements:manage`.\n- **Errors:** `NOT_FOUND` when the agreement is not in this tenant; `INVARIANT_VIOLATION` when the change would break\n  an enforced invariant.\n\nIts name disappears from `principal.agreements` and, if it was required, it stops counting toward\n`principal.pendingAgreements`. The acceptance history is gone with it; the audit log keeps the `agreement:accept`\nevents.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agreementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agreementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/list": {
   "post": {
    "operationId": "agreements.list",
    "tags": [
     "agreements"
    ],
    "summary": "Lists the tenant's agreements by name, with their full text, version, and settings.",
    "description": "- **Permission:** `iam:agreements:read` on the tenant.\n- **Audited as:** `iam:agreements:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Agreement"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/listMine": {
   "post": {
    "operationId": "agreements.listMine",
    "tags": [
     "agreements"
    ],
    "summary": "Returns every agreement of the tenant with the text and whether you have accepted its current version.",
    "description": "- **Permission:** None beyond an ordinary session of the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` from a role session or another tenant's session.\n\nAgreements you still owe come first, required ones before optional ones. Each entry carries `accepted` plus, when you\naccepted some version, `acceptedAt` and `acceptedVersion`, so you can tell \"never accepted\" from \"accepted an older\nversion\". Use it to render a banner or an acceptance screen; `useAgreements` does this in React and Vue apps.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/MyAgreement"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/status": {
   "post": {
    "operationId": "agreements.status",
    "tags": [
     "agreements"
    ],
    "summary": "Reports who accepted an agreement's current version and which active people still owe it.",
    "description": "- **Permission:** `iam:agreements:read` on the agreement.\n- **Audited as:** `iam:agreements:read`.\n- **Errors:** `NOT_FOUND` when the agreement is not in this tenant.\n\n`accepted` lists people with a current acceptance (version and time). `pending` lists every other active person,\nwith `acceptedVersion` when they accepted an older version or their acceptance lapsed. Only people are reported, not\nservice accounts. Use it to chase stragglers before you turn on a policy that denies on\n`principal.pendingAgreements`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#status"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agreementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "agreementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AgreementStatus"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/agreements/update": {
   "post": {
    "operationId": "agreements.update",
    "tags": [
     "agreements"
    ],
    "summary": "Edits an agreement, optionally publishing the change as a new version that everyone must accept again.",
    "description": "- **Permission:** `iam:agreements:manage` on the agreement.\n- **Audited as:** `iam:agreements:manage`.\n- **Errors:** `NOT_FOUND` when the agreement is not in this tenant; `CONFLICT` for a name another agreement uses;\n  `INVALID_INPUT` for the same validation as `create`; `INVARIANT_VIOLATION` when the change would break an enforced\n  invariant.\n\nFields you omit keep their values. `newVersion: true` increments the version, so every existing acceptance stops\ncounting; without it acceptances stay valid even if you change the text. `reacceptAfterDays: null` removes the lapse,\nand an empty `url` removes the link. A new `reacceptAfterDays` applies to existing acceptances at once, measured from\nwhen each was given. Policies match `principal.agreements` by name, so renaming an agreement changes which\nstatements match it.\n\n```ts\n// Material change: everyone accepts again.\nawait iam.api.agreements.update(credential, {\n  tenantId,\n  agreementId,\n  content: 'Use company systems for work. Report incidents within 4 hours.',\n  newVersion: true,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/agreements#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "name": {
            "type": "string"
           },
           "content": {
            "type": "string"
           },
           "url": {
            "type": "string"
           },
           "required": {
            "type": "boolean"
           }
          },
          "additionalProperties": false
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "agreementId": {
            "type": "string"
           },
           "newVersion": {
            "type": "boolean"
           },
           "reacceptAfterDays": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "type": "number"
             }
            ]
           }
          },
          "required": [
           "tenantId",
           "agreementId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Agreement"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/analysis/findings": {
   "post": {
    "operationId": "analysis.findings",
    "tags": [
     "analysis"
    ],
    "summary": "Runs every access-analysis check on the tenant and returns the findings, most severe first, with counts per severity.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `INVALID_INPUT` when `dormantDays` is outside 1 to 3650.\n\n`dormantDays` (default 90) sets when an unused account, API key, or eligible binding is reported. Suppressed\nfindings are left out unless `includeSuppressed` is `true`. Run it on a schedule and alert on new high findings;\nthe `analyze` [CLI command](/docs/reference/cli#analyze) does this with `--fail-on high`, which suits a CI or cron\njob.\n\n```ts\nconst { summary, findings } = await iam.api.analysis.findings(credential, { tenantId, dormantDays: 60 });\nfor (const finding of findings.filter((item) => item.severity === 'high'))\n  console.log(finding.title, finding.detail);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/analysis#findings"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "dormantDays": {
          "type": "number"
         },
         "includeSuppressed": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "generatedAt": {
             "type": "number"
            },
            "dormantDays": {
             "type": "number"
            },
            "summary": {
             "type": "object",
             "properties": {
              "high": {
               "type": "number"
              },
              "medium": {
               "type": "number"
              },
              "low": {
               "type": "number"
              },
              "suppressed": {
               "type": "number"
              }
             },
             "required": [
              "high",
              "medium",
              "low",
              "suppressed"
             ],
             "additionalProperties": false
            },
            "findings": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/AccessFinding"
             }
            }
           },
           "required": [
            "generatedAt",
            "dormantDays",
            "summary",
            "findings"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/analysis/lintPolicy": {
   "post": {
    "operationId": "analysis.lintPolicy",
    "tags": [
     "analysis"
    ],
    "summary": "Checks a policy document, or a stored policy, for errors and for statements that likely do not do what they say.",
    "description": "- **Permission:** `iam:policies:read` on the policy (with `policyId`) or on the tenant (with a candidate\n  `document`).\n- **Audited as:** `iam:policies:read`.\n- **Errors:** `INVALID_INPUT` unless exactly one of `document` and `policyId` is given; `NOT_FOUND` when the\n  policy is not in this tenant.\n\nThe document is first validated the way storage would validate it, including unknown actions and resource types.\nA document storage would reject is not thrown as an error: the result has `valid: false`, the `error` code and\nmessage, and no warnings. A valid document gets `warnings`, each with a `code`, a `severity` (`warning`: probably a\nmistake; `info`: worth a look, often intended), the statement index, and a message. Examples include an allow that\nmakes every holder a full administrator, a condition key the server never sets, a deny that silently never applies\nwhen an optional key is missing, an allow that an unconditional deny shadows, and duplicate statements.\n\nUse it in a policy editor before saving, or in CI over documents kept in version control. Pass `contextKeys` to\nname keys your application supplies through `resolveContext`, so they are not reported as unknown. See\n[policies](/docs/guides/authorization/policies) and [conditions](/docs/guides/authorization/conditions).\n\n```ts\nconst result = await iam.api.analysis.lintPolicy(credential, {\n  tenantId,\n  document: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['documents:*'], resources: ['*'] }],\n  },\n});\n// result.valid === true; result.warnings[0].code === 'service-wildcard'\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/analysis#lintpolicy"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "document": {},
         "policyId": {
          "type": "string"
         },
         "contextKeys": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PolicyLintResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/analysis/suppress": {
   "post": {
    "operationId": "analysis.suppress",
    "tags": [
     "analysis"
    ],
    "summary": "Hides one finding from future results and records why.",
    "description": "- **Permission:** `iam:analysis:update` on `iam/analysis/{findingId}`.\n- **Audited as:** `iam:analysis:update`.\n- **Errors:** `INVALID_INPUT` when `findingId` is not a 24-character finding ID, or `reason` is empty or longer\n  than 500 characters.\n\nThe reason, the caller, and the time are kept and shown to anyone who lists suppressed findings, so reviewers can\nsee who accepted which risk. Suppressing a finding again replaces its reason.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/analysis#suppress"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "findingId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "findingId",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "suppressed": {
             "type": "boolean"
            }
           },
           "required": [
            "suppressed"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/analysis/unsuppress": {
   "post": {
    "operationId": "analysis.unsuppress",
    "tags": [
     "analysis"
    ],
    "summary": "Shows a suppressed finding again.",
    "description": "- **Permission:** `iam:analysis:update` on `iam/analysis/{findingId}`.\n- **Audited as:** `iam:analysis:update`.\n\nUnsuppressing a finding that is not suppressed succeeds and changes nothing.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/analysis#unsuppress"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "findingId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "findingId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "suppressed": {
             "type": "boolean"
            }
           },
           "required": [
            "suppressed"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/assertions/issue": {
   "post": {
    "operationId": "assertions.issue",
    "tags": [
     "assertions"
    ],
    "summary": "Issues a signed assertion about the caller for one audience, valid for five minutes by default.",
    "description": "- **Permission:** `iam:assertions:create` on `iam/{audience}`.\n- **Audited as:** `iam:assertions:create`, on the audience.\n- **Errors:** `INVALID_INPUT` when `audience` is not a URL-safe identifier, `ttlSeconds` is outside 10 to 3600, or\n  `claims` exceeds 4 KiB of JSON or reuses a standard claim name; `ACCESS_DENIED` when the caller may not obtain\n  assertions for that audience, or presents a session token (`sts.getSessionToken`) restricted by a session policy,\n  including one inherited from a scoped API key (recorded as a denial).\n\nBecause the permission is checked on the audience, administrators decide which roles may obtain tokens for which\nservices: allow `iam:assertions:create` on `iam/reports` for analysts, and on `iam/billing-worker` only for the\nbilling role. The audience starts with a letter or digit and may contain letters, digits, `.`, `_`, `:`, `/`, and\n`-`, up to 256 characters. For an assumed-role session, `roles` holds only the assumed role and `groups` is empty.\nRestricted session tokens are refused because the `roles` claim would describe more access than the token allows.\n\n```ts\n// In your API: forward the caller to the reports service.\nconst { token, expiresAt } = await iam.api.assertions.issue(credential, {\n  tenantId,\n  audience: 'reports',\n  ttlSeconds: 120,\n  claims: { requestId },\n});\nawait fetch('https://reports.internal/run', { headers: { authorization: `Bearer ${token}` } });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/assertions#issue"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "audience": {
          "type": "string"
         },
         "ttlSeconds": {
          "type": "number"
         },
         "claims": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "description": "Any JSON value"
          }
         }
        },
        "required": [
         "tenantId",
         "audience"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "token": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            },
            "claims": {
             "$ref": "#/components/schemas/AssertionClaims"
            }
           },
           "required": [
            "token",
            "expiresAt",
            "claims"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/audit/export": {
   "post": {
    "operationId": "audit.export",
    "tags": [
     "audit"
    ],
    "summary": "Returns a page of the tenant's audit events in chain order as JSON Lines, ready to archive.",
    "description": "- **Permission:** `iam:audit:read` on the tenant.\n- **Audited as:** `iam:audit:read`.\n- **Errors:** `INVALID_INPUT` when `fromSequence` is below 1 or `limit` is outside 1 to 10 000.\n\n`body` holds one JSON event per line, including `sequence`, `previousHash`, and `hash`, so an archive can be\nverified later anywhere with `verifyAuditChain` from `@better-iam/core`. Start at `fromSequence` (default 1) and\nfollow `nextSequence` until it is `undefined`; each page's last hash links to the next page's first event. `head`\nis the current end of the chain, useful for comparing against your archive. Archive pages as they are; exporting\nbefore [pruning](/docs/reference/api#pruneaudit) keeps the full history verifiable.\n\n```ts\nlet fromSequence: number | undefined = 1;\nwhile (fromSequence !== undefined) {\n  const page = await iam.api.audit.export(credential, { tenantId, fromSequence, limit: 5000 });\n  if (page.count) await archive.append(`${page.body}\\n`);\n  fromSequence = page.nextSequence;\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/audit#export"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "fromSequence": {
          "type": "number"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "format": {
             "type": "string",
             "const": "jsonl"
            },
            "count": {
             "type": "number"
            },
            "body": {
             "type": "string"
            },
            "firstSequence": {
             "type": "number"
            },
            "lastSequence": {
             "type": "number"
            },
            "nextSequence": {
             "type": "number"
            },
            "head": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "object",
               "properties": {
                "sequence": {
                 "type": "number"
                },
                "hash": {
                 "type": "string"
                }
               },
               "required": [
                "sequence",
                "hash"
               ],
               "additionalProperties": false
              }
             ]
            }
           },
           "required": [
            "format",
            "count",
            "body",
            "firstSequence",
            "lastSequence",
            "nextSequence",
            "head"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/audit/list": {
   "post": {
    "operationId": "audit.list",
    "tags": [
     "audit"
    ],
    "summary": "Searches the tenant's audit events, newest first, by actor, action, resource, outcome, and time range.",
    "description": "- **Permission:** `iam:audit:read` on the tenant.\n- **Audited as:** `iam:audit:read`.\n- **Errors:** `INVALID_INPUT` when `outcome` is not `allow` or `deny`, or `limit` (1 to 1000), `offset`, `from`, or\n  `to` is out of range.\n\n`action` accepts a glob pattern such as `iam:bindings:*` or `binding:*`; `actorId`, `resourceId`, and `outcome`\nmatch exactly; `from` and `to` bound the timestamp (epoch milliseconds). `limit` defaults to 100. Use it for an\nactivity feed, a person's history, or an investigation such as \"every denial in the last hour\".\n\n```ts\nconst denials = await iam.api.audit.list(credential, {\n  tenantId,\n  outcome: 'deny',\n  from: Date.now() - 60 * 60 * 1000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/audit#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         },
         "offset": {
          "type": "number"
         },
         "actorId": {
          "type": "string"
         },
         "action": {
          "type": "string"
         },
         "resourceId": {
          "type": "string"
         },
         "outcome": {
          "type": "string",
          "enum": [
           "allow",
           "deny"
          ]
         },
         "from": {
          "type": "number"
         },
         "to": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AuditEvent"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/audit/verify": {
   "post": {
    "operationId": "audit.verify",
    "tags": [
     "audit"
    ],
    "summary": "Checks the tenant's audit hash chain and reports whether any stored event was altered, reordered, or removed.",
    "description": "- **Permission:** `iam:audit:read` on the tenant.\n- **Audited as:** `iam:audit:read`.\n- **Errors:** `INVALID_INPUT` when `fromSequence` or `toSequence` is below 1.\n\nThe check walks the events in sequence order: the sequences must be contiguous, each `previousHash` must equal\nthe previous event's hash, and every hash must be recomputable from its event. A full verification also requires\nthe stored chain head to match the last event, which catches events deleted from the end. `fromSequence` and\n`toSequence` verify a window against its own links only, without the head comparison.\n\n`valid` is the answer. On failure, `failure` names the sequence, the event ID, and the reason:\n`sequence-gap`, `previous-hash-mismatch`, `hash-mismatch`, or `head-mismatch`. `checked` counts verified events,\nand `unchained` counts older events recorded before the chain existed, which are never failures. After a prune the\nchain starts at a later sequence, and verification starts from there.\n\nSchedule it and alert when `valid` is `false`. A chain proves nothing was changed by someone without write access\nto both the events and the chain head; it cannot stop someone with full database access from rewriting both, so\nalso export regularly to independent storage and compare heads.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/audit#verify"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "fromSequence": {
          "type": "number"
         },
         "toSequence": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "head": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "object",
               "properties": {
                "sequence": {
                 "type": "number"
                },
                "hash": {
                 "type": "string"
                },
                "updatedAt": {
                 "type": "number"
                }
               },
               "required": [
                "sequence",
                "hash",
                "updatedAt"
               ],
               "additionalProperties": false
              }
             ]
            },
            "valid": {
             "type": "boolean"
            },
            "checked": {
             "type": "number"
            },
            "unchained": {
             "type": "number"
            },
            "first": {
             "type": "number"
            },
            "last": {
             "type": "number"
            },
            "lastHash": {
             "type": "string"
            },
            "failure": {
             "type": "object",
             "properties": {
              "sequence": {
               "type": "number"
              },
              "id": {
               "type": "string"
              },
              "reason": {
               "$ref": "#/components/schemas/AuditChainFailure"
              }
             },
             "required": [
              "sequence",
              "id",
              "reason"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "head",
            "valid",
            "checked",
            "unchained"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/beginMfa": {
   "post": {
    "operationId": "auth.beginMfa",
    "tags": [
     "auth"
    ],
    "summary": "Starts enrolling a TOTP authenticator and returns its secret and an otpauth:// URI to show as a QR code.",
    "description": "- **Permission:** None: public during sign-in, with the `{ tenantId, challenge }` of an `mfaRequired` result; or the caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** not audited; `confirmMfa` records `auth:mfa:enable`.\n- **Errors:** `INVALID_CHALLENGE` when the sign-in challenge is invalid or expired; `MFA_REQUIRED` when a sign-in challenge is used by someone who already has an authenticator (they must use it instead); `MFA_ALREADY_ENABLED` (409) when a signed-in caller already has one; `RECENT_AUTH_REQUIRED` for an older session.\n\nThe enrollment stays pending for ten minutes and is bound to the credential that started it, so `confirmMfa` must present the same sign-in challenge or the same session. Calling `beginMfa` again replaces a pending enrollment with a new secret. Over HTTP, send `{ tenantId, challenge }` in the body during sign-in, or `{}` with a session.\n\n```ts\n// signIn returned { mfaRequired: true, enrollmentRequired: true, challenge }.\nconst { secret, uri } = await iam.api.auth.beginMfa({ tenantId, challenge: result.challenge });\n// Render `uri` as a QR code and show `secret` for manual entry, then call confirmMfa.\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#beginmfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "secret": {
             "type": "string"
            },
            "uri": {
             "type": "string"
            }
           },
           "required": [
            "secret",
            "uri"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/beginPasskeyAuthentication": {
   "post": {
    "operationId": "auth.beginPasskeyAuthentication",
    "tags": [
     "auth"
    ],
    "summary": "Starts a passkey sign-in and returns WebAuthn request options plus the challengeId to finish it with.",
    "description": "- **Permission:** None: public.\n- **Audited as:** not audited; `finishPasskeyAuthentication` records the session.\n- **Errors:** `FEATURE_DISABLED` when passkeys are not configured; `METHOD_NOT_ALLOWED` when the tenant does not allow `passkey`; `INVALID_CREDENTIALS` when `email` names no active person in the tenant; `RATE_LIMITED`.\n\nWithout `email`, the options name no credential: the browser's passkey picker or autofill offers any discoverable passkey it holds for your site, and `finishPasskeyAuthentication` finds the account from the credential itself. That mode is rate limited per client address with ten times the ordinary allowance, because a login page starts one on every visit. With `email`, the options list that person's passkeys. The challenge is valid for five minutes, so refresh an autofill request that waits longer. See [passkeys](/docs/guides/authentication/passkeys).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#beginpasskeyauthentication"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "challengeId": {
             "type": "string"
            },
            "options": {}
           },
           "required": [
            "challengeId",
            "options"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/beginPasskeyMfa": {
   "post": {
    "operationId": "auth.beginPasskeyMfa",
    "tags": [
     "auth"
    ],
    "summary": "Starts answering an MFA challenge with a registered passkey instead of a code, returning WebAuthn request options bound to that sign-in.",
    "description": "- **Permission:** None: public, with the `challenge` of an `mfaRequired` result that offered `passkeyAvailable`.\n- **Audited as:** not audited; `finishPasskeyMfa` records the session.\n- **Errors:** `FEATURE_DISABLED` when passkeys are not configured or the person has none registered; `INVALID_CHALLENGE` when the sign-in challenge is invalid or expired; `RATE_LIMITED` (counted per challenge and per person).\n\nThe passkey challenge it returns is valid for five minutes, and the sign-in challenge must still be open when you call `finishPasskeyMfa`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#beginpasskeymfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challenge": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "challenge"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "challengeId": {
             "type": "string"
            },
            "options": {}
           },
           "required": [
            "challengeId",
            "options"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/beginPasskeyRegistration": {
   "post": {
    "operationId": "auth.beginPasskeyRegistration",
    "tags": [
     "auth"
    ],
    "summary": "Returns WebAuthn creation options for adding a passkey to the caller's account.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** not audited; `finishPasskeyRegistration` records `auth:passkey:create`.\n- **Errors:** `FEATURE_DISABLED` when passkeys are not configured; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nThe options require a discoverable credential and user verification, request no attestation, and exclude the passkeys the person already has. The challenge is bound to this session and valid for five minutes.\n\n```ts\n// In the browser, with the typed client.\nimport { startRegistration } from 'better-iam/client/passkeys';\n\nconst { challengeId, options } = await client.auth.beginPasskeyRegistration();\nconst response = await startRegistration({ optionsJSON: options });\nawait client.auth.finishPasskeyRegistration({ challengeId, response, name: 'Work laptop' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#beginpasskeyregistration"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "challengeId": {
             "type": "string"
            },
            "options": {}
           },
           "required": [
            "challengeId",
            "options"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/changePassword": {
   "post": {
    "operationId": "auth.changePassword",
    "tags": [
     "auth"
    ],
    "summary": "Replaces the caller's password after checking the current one, and signs the person out everywhere.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:password:change`; a wrong current password is recorded as `auth:signin:fail`.\n- **Errors:** `INVALID_CREDENTIALS` when `currentPassword` is wrong or the account has no password; `WEAK_PASSWORD`, `BREACHED_PASSWORD`, or `PASSWORD_REUSED` when the new password fails the deployment's screening or the tenant's rules; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`; `RATE_LIMITED`.\n\nEvery session ends, including the one that made the call, along with remembered devices and pending challenges, so the person signs in again with the new password. A wrong current password counts as a failed attempt on the account, because someone holding a stolen session may be guessing it. People who have no password set one through `requestPasswordReset` and `resetPassword` instead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#changepassword"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "currentPassword": {
          "type": "string"
         },
         "password": {
          "type": "string"
         }
        },
        "required": [
         "currentPassword",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/confirmEmailChange": {
   "post": {
    "operationId": "auth.confirmEmailChange",
    "tags": [
     "auth"
    ],
    "summary": "Completes an email change from the link sent to the new address, then signs the person out everywhere.",
    "description": "- **Permission:** None: public (the token from the `email-change` email is the proof).\n- **Audited as:** `auth:email:change`.\n- **Errors:** `INVALID_CHALLENGE` when the token is invalid, already used, or older than ten minutes; `UNAUTHENTICATED` when the session that requested the change has ended; `IDENTITY_EXISTS` (409) when another person in the tenant now has the address; `RATE_LIMITED`.\n\nThe new address is marked verified, since following the link proves the person receives mail there. Tying the confirmation to the requesting session means a change requested from a session that has since been revoked cannot complete. Every session, remembered device, and pending challenge of the person then ends.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#confirmemailchange"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "token": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "token"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/confirmMfa": {
   "post": {
    "operationId": "auth.confirmMfa",
    "tags": [
     "auth"
    ],
    "summary": "Checks the first code from a newly enrolled authenticator, turns MFA on, and returns a fresh session with ten recovery codes.",
    "description": "- **Permission:** None: public with the sign-in challenge (`credential: { tenantId, challenge }`), or the caller's own session, [authenticated recently](#sessions-and-recent-authentication); either way, the same credential that called `beginMfa`.\n- **Audited as:** `auth:mfa:enable`, then `auth:session:create` (and `auth:device:trust` with `rememberDevice`).\n- **Errors:** `INVALID_CHALLENGE` when there is no pending enrollment for this credential or it is older than ten minutes; `INVALID_MFA` for a wrong or already-used code; `MFA_REQUIRED` when a sign-in challenge is used by someone who already has an authenticator; `RECENT_AUTH_REQUIRED`; `RATE_LIMITED`.\n\nShow the recovery codes once and ask the person to store them somewhere safe: they are returned only here and by `regenerateRecoveryCodes`, and each works once with `recoverMfa`. Enabling MFA ends every existing session, remembered device, and pending challenge of the person, so the returned session is the only one left. It has passed MFA and keeps the original sign-in method; over HTTP it replaces the session cookie.\n\n```ts\nconst { token, recoveryCodes } = await iam.api.auth.confirmMfa({\n  credential: { tenantId, challenge: result.challenge },\n  code: '492039',\n  rememberDevice: true,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#confirmmfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "credential": {
          "$ref": "#/components/schemas/MfaCredential"
         },
         "code": {
          "type": "string"
         },
         "rememberDevice": {
          "type": "boolean"
         }
        },
        "required": [
         "credential",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/SessionResult"
            },
            {
             "type": "object",
             "properties": {
              "deviceToken": {
               "type": "string"
              },
              "deviceExpiresAt": {
               "type": "number"
              }
             },
             "additionalProperties": false
            },
            {
             "type": "object",
             "properties": {
              "recoveryCodes": {
               "type": "array",
               "items": {
                "type": "string"
               }
              }
             },
             "required": [
              "recoveryCodes"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/confirmPhoneVerification": {
   "post": {
    "operationId": "auth.confirmPhoneVerification",
    "tags": [
     "auth"
    ],
    "summary": "Checks the six-digit SMS code from startPhoneVerification and saves the number as the caller's verified phone.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication); the same session that started the verification.\n- **Audited as:** `auth:phone:verify`.\n- **Errors:** `INVALID_CHALLENGE` when the code is wrong or expired, or was sent to another session or another number; `PHONE_EXISTS` (409) when another person in the tenant has already verified the number; `INVALID_INPUT` when `phone` is not in E.164 format; `RECENT_AUTH_REQUIRED`; `RATE_LIMITED`.\n\nA verified phone lets the person sign in with SMS codes when the deployment enables `passwordlessSms`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#confirmphoneverification"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "phone": {
          "type": "string"
         },
         "code": {
          "type": "string"
         }
        },
        "required": [
         "phone",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/deletePasskey": {
   "post": {
    "operationId": "auth.deletePasskey",
    "tags": [
     "auth"
    ],
    "summary": "Removes one of the caller's passkeys and signs the person out everywhere.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:passkey:delete`.\n- **Errors:** `NOT_FOUND` when the passkey is not one of the caller's; `LAST_AUTHENTICATOR` (409) when it is the last passkey and the person has no other way to sign in; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\n\"Another way to sign in\" means a password, or a verified email address or phone number with passwordless sign-in enabled for that channel. Every session, remembered device, and pending challenge ends, so no session opened with the removed passkey outlives it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#deletepasskey"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "id": {
          "type": "string"
         }
        },
        "required": [
         "id"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/disableMfa": {
   "post": {
    "operationId": "auth.disableMfa",
    "tags": [
     "auth"
    ],
    "summary": "Removes the caller's authenticator and recovery codes, and signs the person out everywhere.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication) and signed in with MFA.\n- **Audited as:** `auth:mfa:disable`.\n- **Errors:** `MFA_REQUIRED` when the session did not pass MFA, when the person is a root administrator, or when the tenant policy or the deployment's `requireMfa` callback requires MFA for them; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nRegistered passkeys are kept. Every session, remembered device, and pending challenge of the person ends.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#disablemfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/finishPasskeyAuthentication": {
   "post": {
    "operationId": "auth.finishPasskeyAuthentication",
    "tags": [
     "auth"
    ],
    "summary": "Verifies a passkey assertion and signs the person in with a session that has already passed MFA.",
    "description": "- **Permission:** None: public.\n- **Audited as:** `auth:session:create` with method `passkey`.\n- **Errors:** `INVALID_CHALLENGE` when `challengeId` is invalid or expired; `INVALID_PASSKEY` when the passkey is not registered to an account in this tenant, its user handle does not match, or verification fails; `INVALID_INPUT` when `response` is missing; `METHOD_NOT_ALLOWED`; `FEATURE_DISABLED`; `EMAIL_UNVERIFIED`, `IP_NOT_ALLOWED`, or `IP_BLOCKED` from the session checks; `RATE_LIMITED`.\n\nThe server checks the challenge, origin, relying-party ID, user verification, signature, and signature counter, then records the passkey's `lastUsedAt`. Because the passkey proves possession and user verification together, the result is always a session (`SessionResult`), never an MFA challenge. Over HTTP it sets the session cookie.\n\n```ts\n// In the browser, with the typed client.\nimport { startAuthentication } from 'better-iam/client/passkeys';\n\nconst { challengeId, options } = await client.auth.beginPasskeyAuthentication({ tenantId });\nconst response = await startAuthentication({ optionsJSON: options });\nawait client.auth.finishPasskeyAuthentication({ tenantId, challengeId, response });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#finishpasskeyauthentication"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challengeId": {
          "type": "string"
         },
         "response": {}
        },
        "required": [
         "tenantId",
         "challengeId",
         "response"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SessionResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/finishPasskeyMfa": {
   "post": {
    "operationId": "auth.finishPasskeyMfa",
    "tags": [
     "auth"
    ],
    "summary": "Verifies a passkey assertion for a pending sign-in and issues the session, optionally remembering the device.",
    "description": "- **Permission:** None: public, with the `challengeId` from `beginPasskeyMfa`.\n- **Audited as:** `auth:session:create` (and `auth:device:trust` with `rememberDevice`).\n- **Errors:** `INVALID_CHALLENGE` when the passkey challenge, or the sign-in challenge it belongs to, is invalid or expired; `INVALID_PASSKEY` when the passkey is not the person's or verification fails; `INVALID_INPUT` when `response` is missing; `RATE_LIMITED` (counted per challenge and per person).\n\nBoth challenges are consumed together, and the session keeps the method of the first factor, such as `password`. The result is an `MfaSessionResult`: the session, plus `deviceToken` and `deviceExpiresAt` when you asked to remember the device and the policy allows it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#finishpasskeymfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challengeId": {
          "type": "string"
         },
         "response": {},
         "rememberDevice": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "challengeId",
         "response"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MfaSessionResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/finishPasskeyRegistration": {
   "post": {
    "operationId": "auth.finishPasskeyRegistration",
    "tags": [
     "auth"
    ],
    "summary": "Verifies the browser's registration response and saves the new passkey on the caller's account.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication); the same session that called `beginPasskeyRegistration`.\n- **Audited as:** `auth:passkey:create`, with the passkey's name.\n- **Errors:** `INVALID_CHALLENGE` when the challenge is expired or belongs to another session; `INVALID_PASSKEY` when verification fails; `PASSKEY_EXISTS` (409) when the credential is already registered anywhere in the installation; `INVALID_INPUT` for an empty name or one over 64 characters; `RECENT_AUTH_REQUIRED`.\n\n`name` is optional. Without it, the passkey is labeled from what the authenticator reports about itself: \"This device\", \"Phone\", \"Security key\", or \"Passkey\". Returns the new passkey's `id` and `name`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#finishpasskeyregistration"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "challengeId": {
          "type": "string"
         },
         "response": {},
         "name": {
          "type": "string"
         }
        },
        "required": [
         "challengeId",
         "response"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "name": {
             "type": "string"
            }
           },
           "required": [
            "id",
            "name"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/finishPasswordless": {
   "post": {
    "operationId": "auth.finishPasswordless",
    "tags": [
     "auth"
    ],
    "summary": "Redeems a magic-link token or one-time code and signs the person in, or returns an MFA challenge.",
    "description": "- **Permission:** None: public.\n- **Audited as:** `auth:session:create` when a session is issued.\n- **Errors:** `INVALID_CHALLENGE` when the token is wrong, already used, older than five minutes, or issued for another destination, or when the person's address or phone changed since it was sent; `METHOD_NOT_ALLOWED`; `FEATURE_DISABLED` when the channel has been turned off; `RATE_LIMITED` (counted per destination).\n\nPass the same `destination` the message went to: a value starting with `+` is read as a phone number, anything else as an email address. Signing in by email also marks the address verified. The result is a `SignInResult` (see [the sign-in flow](#sign-in-flow-and-mfa-challenges)); a `deviceToken` from \"remember this device\" satisfies MFA, and the HTTP handler adds it from the device cookie.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#finishpasswordless"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "destination": {
          "type": "string"
         },
         "token": {
          "type": "string"
         },
         "deviceToken": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "destination",
         "token"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SignInResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/getSession": {
   "post": {
    "operationId": "auth.getSession",
    "tags": [
     "auth"
    ],
    "summary": "Returns the caller's identity, their session, and the session limits in force, so a client can warn before an idle sign-out.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n- **Errors:** `UNAUTHENTICATED` when the session is missing, expired, idle, or revoked.\n\n`limits` holds the tenant's `lifetimeMs` and `idleTimeoutMs`, `idleExpiresAt` (when the session lapses if nothing uses it again, never later than its absolute expiry), and `now`, the server's clock, so a client can correct for its own clock skew. Like every authenticated call, it counts as activity, which makes it the natural target for a \"Stay signed in\" button. The identity comes back without its password hash.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#getsession"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "identity": {
             "$ref": "#/components/schemas/SafeIdentity"
            },
            "session": {
             "$ref": "#/components/schemas/SafeSession"
            },
            "limits": {
             "type": "object",
             "properties": {
              "lifetimeMs": {
               "type": "number"
              },
              "idleTimeoutMs": {
               "type": "number"
              },
              "idleExpiresAt": {
               "type": "number"
              },
              "now": {
               "type": "number"
              }
             },
             "required": [
              "lifetimeMs",
              "idleTimeoutMs",
              "idleExpiresAt",
              "now"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "identity",
            "session",
            "limits"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/listPasskeys": {
   "post": {
    "operationId": "auth.listPasskeys",
    "tags": [
     "auth"
    ],
    "summary": "Lists the caller's passkeys, newest first, without key material.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n\nEach entry has its `id`, `name`, `createdAt`, `lastUsedAt` (the last sign-in or MFA answer), `deviceType` (`singleDevice`, or `multiDevice` for a synced passkey), `backedUp`, `transports`, and, when the authenticator reports one, the `aaguid` that identifies its model.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#listpasskeys"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/SafePasskey"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/listSecurityEvents": {
   "post": {
    "operationId": "auth.listSecurityEvents",
    "tags": [
     "auth"
    ],
    "summary": "Returns the caller's own authentication trail, newest first: sign-ins, failed attempts, sign-outs, and changes to passwords, addresses, factors, passkeys, and devices.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n- **Errors:** `INVALID_INPUT` when `limit` is not an integer from 1 to 200.\n\nIt returns up to `limit` (50 by default) of the `auth:*` audit events the person was the actor of in this tenant. Each event carries `impersonatorId` when an administrator acted through impersonation, `sequence` (its position in the [audit chain](/docs/guides/events/audit-chain)), and `metadata` such as the client's `ip` and `userAgent`, the sign-in `method`, or a failure's `reason`. Use it for an account page's recent activity; the tenant's full log is [`audit.list`](/docs/reference/api/audit#list), which needs `iam:audit:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#listsecurityevents"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "limit": {
          "type": "number"
         }
        },
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "action": {
              "type": "string"
             },
             "timestamp": {
              "type": "number"
             },
             "impersonatorId": {
              "type": "string"
             },
             "sequence": {
              "type": "number"
             },
             "metadata": {
              "type": "object",
              "properties": {},
              "additionalProperties": {
               "description": "Any JSON value"
              }
             }
            },
            "required": [
             "id",
             "action",
             "timestamp"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/listSessions": {
   "post": {
    "operationId": "auth.listSessions",
    "tags": [
     "auth"
    ],
    "summary": "Lists the caller's unexpired sessions in this tenant, marking the one making the call with current: true.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n\nEach session shows its sign-in `method`, its `client` details, and its timestamps, never its token. Sessions an administrator opened as the person through impersonation appear too, with `impersonatorId`, so people can see them and end them with `revokeSession`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#listsessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "$ref": "#/components/schemas/SafeSession"
             },
             {
              "type": "object",
              "properties": {
               "current": {
                "type": "boolean"
               }
              },
              "required": [
               "current"
              ],
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/listTrustedDevices": {
   "post": {
    "operationId": "auth.listTrustedDevices",
    "tags": [
     "auth"
    ],
    "summary": "Lists the caller's remembered devices that have not expired, most recently used first, without their tokens.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n\nEach device records when it was remembered, when it expires, when it last vouched for a sign-in (`lastUsedAt`), and the client (IP address, user agent, label) that completed MFA.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#listtrusteddevices"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/SafeTrustedDevice"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/mfaStatus": {
   "post": {
    "operationId": "auth.mfaStatus",
    "tags": [
     "auth"
    ],
    "summary": "Summarizes what the caller has set up: whether an authenticator is enabled, how many recovery codes remain, how many passkeys and remembered devices they have, and whether this session passed MFA.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** not audited.\n\nUse it to drive an account security page, for example to prompt for new recovery codes when `recoveryCodesRemaining` runs low.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#mfastatus"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "enabled": {
             "type": "boolean"
            },
            "recoveryCodesRemaining": {
             "type": "number"
            },
            "passkeys": {
             "type": "number"
            },
            "trustedDevices": {
             "type": "number"
            },
            "sessionMfa": {
             "type": "boolean"
            }
           },
           "required": [
            "enabled",
            "recoveryCodesRemaining",
            "passkeys",
            "trustedDevices",
            "sessionMfa"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/reauthenticate": {
   "post": {
    "operationId": "auth.reauthenticate",
    "tags": [
     "auth"
    ],
    "summary": "Checks the caller's password again and returns a new session that counts as recently authenticated, or an MFA challenge.",
    "description": "- **Permission:** The caller's own session, but not an impersonation session.\n- **Audited as:** `auth:session:create` for the new session; a wrong password is recorded as `auth:signin:fail`.\n- **Errors:** `INVALID_CREDENTIALS` for a wrong password or an account without one; `METHOD_NOT_ALLOWED` when the tenant does not allow `password`; `IMPERSONATION_RESTRICTED`; `RATE_LIMITED`.\n\nCall it when an operation answers `RECENT_AUTH_REQUIRED`, then retry with the new session. It is a full sign-in: when the person needs MFA the result is an `mfaRequired` challenge (a remembered device does not skip it here), and the session comes from `verifyMfa` or another second-factor call. The old session stays valid; over HTTP the cookie switches to the new one. People without a password get a recent session by signing in again, for example with a passkey.\n\n```ts\nconst result = await iam.api.auth.reauthenticate({ headers: request.headers }, { password });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#reauthenticate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "password": {
          "type": "string"
         }
        },
        "required": [
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SignInResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/recoverMfa": {
   "post": {
    "operationId": "auth.recoverMfa",
    "tags": [
     "auth"
    ],
    "summary": "Answers an MFA challenge with a single-use recovery code when the person has lost their authenticator.",
    "description": "- **Permission:** None: public, with the `challenge` of an `mfaRequired` result.\n- **Audited as:** `auth:mfa:recover`, then `auth:session:create`; a wrong code is recorded as `auth:signin:fail`.\n- **Errors:** `INVALID_MFA` when the code is wrong or already used, or the person has no authenticator enrolled; `INVALID_CHALLENGE` when the challenge is invalid or expired; `RATE_LIMITED` (counted per challenge and per person).\n\nThe code is used up. The authenticator stays enrolled, so once signed in the person should check `mfaStatus` and replace their codes with `regenerateRecoveryCodes`. This call cannot remember the device.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#recovermfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challenge": {
          "type": "string"
         },
         "code": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "challenge",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SessionResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/regenerateRecoveryCodes": {
   "post": {
    "operationId": "auth.regenerateRecoveryCodes",
    "tags": [
     "auth"
    ],
    "summary": "Replaces all of the caller's recovery codes with ten new ones.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication) and signed in with MFA.\n- **Audited as:** `auth:mfa:recovery-codes`.\n- **Errors:** `MFA_REQUIRED` when the session did not pass MFA; `MFA_NOT_ENROLLED` when no authenticator is enabled; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nThe old codes stop working at once. Show the new ones one time: only their hashes are stored, so they cannot be read back.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#regeneraterecoverycodes"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "recoveryCodes": {
             "type": "array",
             "items": {
              "type": "string"
             }
            }
           },
           "required": [
            "recoveryCodes"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/renamePasskey": {
   "post": {
    "operationId": "auth.renamePasskey",
    "tags": [
     "auth"
    ],
    "summary": "Changes the label of one of the caller's passkeys.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** `auth:passkey:rename`, with the new name.\n- **Errors:** `NOT_FOUND` when the passkey is not one of the caller's; `INVALID_INPUT` for an empty name or one over 64 characters.\n\nUnlike the other passkey changes, renaming does not need recent authentication.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#renamepasskey"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "id": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "id",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SafePasskey"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/requestEmailChange": {
   "post": {
    "operationId": "auth.requestEmailChange",
    "tags": [
     "auth"
    ],
    "summary": "Emails a confirmation link to the new address the caller wants to move to.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** not audited; `confirmEmailChange` records `auth:email:change`.\n- **Errors:** `INVALID_INPUT` for an invalid address; `FEATURE_DISABLED` when email delivery is not configured; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`; `RATE_LIMITED`.\n\nThe address does not change until someone with access to the new mailbox follows the `email-change` link, within ten minutes and while this session is still alive. Whether another person already uses the address is checked at confirmation. Administrators change an address directly with [`identities.update`](/docs/reference/api/identities#update).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#requestemailchange"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "email": {
          "type": "string"
         }
        },
        "required": [
         "email"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/requestEmailVerification": {
   "post": {
    "operationId": "auth.requestEmailVerification",
    "tags": [
     "auth"
    ],
    "summary": "Emails a new verification link to an address that has not been verified yet.",
    "description": "- **Permission:** None: public.\n- **Audited as:** not audited; `verifyEmail` records `auth:email:verify`.\n- **Errors:** `FEATURE_DISABLED` when email delivery is not configured; `RATE_LIMITED`.\n\nIt always succeeds, so it never reveals whether an account exists: the `verify-email` message, valid for 24 hours, goes out only when an active person in the tenant has that address unverified. Use it for a \"resend verification email\" button.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#requestemailverification"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "email"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/requestMfaCode": {
   "post": {
    "operationId": "auth.requestMfaCode",
    "tags": [
     "auth"
    ],
    "summary": "Emails a six-digit one-time code that answers the given MFA challenge, for people with no authenticator.",
    "description": "- **Permission:** None: public, with the `challenge` of a sign-in that offered `emailCodeAvailable`.\n- **Audited as:** not audited.\n- **Errors:** `FEATURE_DISABLED` when emailed codes were not offered for this sign-in or the person has no verified address; `INVALID_CHALLENGE` when the challenge is invalid or expired; `RATE_LIMITED` (counted per challenge and per person).\n\nThe code works once with `verifyMfa` until the returned `expiresAt`, which never outlives the sign-in challenge. Requesting again replaces the code. Emailed codes are offered only to people with nothing enrolled and a verified address, never to root administrators, and only when the tenant's or the deployment's `mfaEmailCodes` allows them.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#requestmfacode"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challenge": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "challenge"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            },
            "expiresAt": {
             "type": "number"
            }
           },
           "required": [
            "success",
            "expiresAt"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/requestPasswordReset": {
   "post": {
    "operationId": "auth.requestPasswordReset",
    "tags": [
     "auth"
    ],
    "summary": "Emails a password-reset link to a person who has forgotten their password.",
    "description": "- **Permission:** None: public.\n- **Audited as:** not audited; `resetPassword` records `auth:password:reset`.\n- **Errors:** `FEATURE_DISABLED` when email delivery is not configured or password sign-in is turned off; `RATE_LIMITED`.\n\nIt always succeeds, so it never reveals whether an account exists. The `password-reset` message goes out only to an active person whose address is verified, and its token is valid for ten minutes. Administrators can send the same email for a member with [`identities.requestPasswordReset`](/docs/reference/api/identities#requestpasswordreset). See [recovery](/docs/guides/authentication/recovery).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#requestpasswordreset"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "email"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/resetPassword": {
   "post": {
    "operationId": "auth.resetPassword",
    "tags": [
     "auth"
    ],
    "summary": "Sets a new password with the token from a password-reset email and ends every session of the person.",
    "description": "- **Permission:** None: public (the token is the proof).\n- **Audited as:** `auth:password:reset`.\n- **Errors:** `INVALID_CHALLENGE` when the token is invalid, already used, or expired; `WEAK_PASSWORD`, `BREACHED_PASSWORD`, or `PASSWORD_REUSED` when the new password fails the deployment's screening or the tenant's rules; `FEATURE_DISABLED` when password sign-in is turned off; `RATE_LIMITED`.\n\nRecovery never signs the person in and never removes MFA: they sign in afterwards with the new password and their second factor. Every session, remembered device, and pending challenge ends, including any other reset links. The new password also restarts the tenant's password-age clock (`passwordMaxAgeDays`), which is how someone whose password expired gets back in.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#resetpassword"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "token": {
          "type": "string"
         },
         "password": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "token",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/revokeOtherSessions": {
   "post": {
    "operationId": "auth.revokeOtherSessions",
    "tags": [
     "auth"
    ],
    "summary": "Ends every other session of the caller in this tenant (\"sign out everywhere else\") and returns how many ended.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:session:revoke-others`.\n- **Errors:** `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nThis includes sessions an administrator opened as the person through impersonation. Remembered devices are not affected, so a device that is still remembered can sign in again without MFA; use `revokeTrustedDevices` to forget those too.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#revokeothersessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/revokeSession": {
   "post": {
    "operationId": "auth.revokeSession",
    "tags": [
     "auth"
    ],
    "summary": "Ends one of the caller's sessions, such as one left open on a shared computer.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:session:revoke`.\n- **Errors:** `NOT_FOUND` when the session is not one of the caller's; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nTake the id from `listSessions`. Any impersonation sessions the person opened through the ended session end with it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#revokesession"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "sessionId": {
          "type": "string"
         }
        },
        "required": [
         "sessionId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/revokeTrustedDevice": {
   "post": {
    "operationId": "auth.revokeTrustedDevice",
    "tags": [
     "auth"
    ],
    "summary": "Forgets one remembered device, so its next sign-in asks for MFA again.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:device:revoke`.\n- **Errors:** `NOT_FOUND` when the device is not one of the caller's; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nSessions already issued on that device keep working; end them with `revokeSession`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#revoketrusteddevice"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "deviceId": {
          "type": "string"
         }
        },
        "required": [
         "deviceId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/revokeTrustedDevices": {
   "post": {
    "operationId": "auth.revokeTrustedDevices",
    "tags": [
     "auth"
    ],
    "summary": "Forgets every remembered device of the caller and returns how many were removed.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** `auth:device:revoke`, once, when at least one device was removed.\n- **Errors:** `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nOver HTTP it also clears the `better-iam.device` cookie in the calling browser. Existing sessions keep working.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#revoketrusteddevices"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/signIn": {
   "post": {
    "operationId": "auth.signIn",
    "tags": [
     "auth"
    ],
    "summary": "Checks an email address and password and returns a session, or an MFA challenge when a second factor is needed.",
    "description": "- **Permission:** None: public.\n- **Audited as:** `auth:session:create` when a session is issued; `auth:signin:fail` when a real, active account was given a wrong password.\n- **Errors:** `INVALID_CREDENTIALS` (401) for an unknown address, a wrong password, an inactive account, or an account without a password, all indistinguishable; `EMAIL_UNVERIFIED` when verification is required and still pending; `PASSWORD_EXPIRED` when the tenant's maximum password age has passed; `METHOD_NOT_ALLOWED`; `TENANT_UNAVAILABLE`; `IP_NOT_ALLOWED` or `IP_BLOCKED`; `FEATURE_DISABLED` when password sign-in is turned off; `RATE_LIMITED`.\n\nThe tenant is never inferred from the email address: you always pass `tenantId`. Unknown addresses go through the same password-hash work as real ones, so response times do not reveal which accounts exist. Pass `deviceToken` from an earlier \"remember this device\" to skip the second factor in that browser. See [the sign-in flow](#sign-in-flow-and-mfa-challenges) for what to do with each result.\n\n```ts\nconst result = await iam.auth.withClient({ ip, userAgent }, () =>\n  iam.api.auth.signIn({ tenantId, email: 'ada@example.com', password }),\n);\nif ('mfaRequired' in result) {\n  // Ask for a code, then: iam.api.auth.verifyMfa({ tenantId, challenge: result.challenge, code })\n} else {\n  // result.token is the bearer token; result.session describes the new session.\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#signin"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         },
         "password": {
          "type": "string"
         },
         "deviceToken": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "email",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SignInResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/signOut": {
   "post": {
    "operationId": "auth.signOut",
    "tags": [
     "auth"
    ],
    "summary": "Ends the caller's current session.",
    "description": "- **Permission:** The caller's own session.\n- **Audited as:** `auth:session:revoke`.\n- **Errors:** `UNAUTHENTICATED` when the session has already ended.\n\nImpersonation sessions the person opened through this session end with it. Over HTTP the session cookie is cleared even when the sign-out is refused, for example because the session had already idled out. Remembered devices stay remembered; `revokeTrustedDevices` forgets them.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#signout"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/signUp": {
   "post": {
    "operationId": "auth.signUp",
    "tags": [
     "auth"
    ],
    "summary": "Registers a new person in a tenant with an email address and password, when self-registration is enabled.",
    "description": "- **Permission:** None: public.\n- **Audited as:** `auth:identity:create`.\n- **Errors:** `FEATURE_DISABLED` when `signUpEnabled` is off or password sign-in is turned off; `FORBIDDEN` for the root tenant, whose people only administrators create; `IDENTITY_EXISTS` (409) when the address is taken in this tenant; `LIMIT_EXCEEDED` when the tenant has reached its member limit; `WEAK_PASSWORD` or `BREACHED_PASSWORD`; `TENANT_UNAVAILABLE`; `RATE_LIMITED`.\n\nSign-up does not sign the person in. When email verification is required (the default once sign-up is enabled), it queues a `verify-email` message valid for 24 hours and returns `verificationRequired: true`, and `signIn` fails with `EMAIL_UNVERIFIED` until `verifyEmail` succeeds. The new person is never an owner or a root administrator, and signing up grants no access by itself: bind roles with [`bindings.create`](/docs/reference/api/bindings#create) or add them to a group.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#signup"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "password": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "email",
         "name",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "identity": {
             "$ref": "#/components/schemas/SafeIdentity"
            },
            "verificationRequired": {
             "type": "boolean"
            }
           },
           "required": [
            "identity",
            "verificationRequired"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/startPasswordless": {
   "post": {
    "operationId": "auth.startPasswordless",
    "tags": [
     "auth"
    ],
    "summary": "Sends a magic link or a one-time code for signing in without a password.",
    "description": "- **Permission:** None: public.\n- **Audited as:** not audited; `finishPasswordless` records the session.\n- **Errors:** `INVALID_INPUT` for an unknown `channel` or `kind`, a malformed destination, or SMS with `kind: 'magic-link'`; `FEATURE_DISABLED` when the channel is not enabled; `METHOD_NOT_ALLOWED` when the tenant does not allow it; `RATE_LIMITED` (counted per destination).\n\nIt always succeeds, whether or not the destination belongs to anyone, so it never reveals which accounts exist. A message goes out only to an active person with that email address, or with that phone number verified; SMS supports codes only. The magic-link token or six-digit code is valid for five minutes and works once, with `finishPasswordless`. The message uses the `magic-link` or `code` template, which your `sendEmail` or `sendSms` callback turns into an email or text.\n\n```ts\nawait iam.api.auth.startPasswordless({\n  tenantId,\n  destination: 'ada@example.com',\n  channel: 'email',\n  kind: 'code',\n});\n// Later, with the code the person typed:\nconst result = await iam.api.auth.finishPasswordless({\n  tenantId,\n  destination: 'ada@example.com',\n  token: '038514',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#startpasswordless"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "destination": {
          "type": "string"
         },
         "channel": {
          "type": "string",
          "enum": [
           "email",
           "sms"
          ]
         },
         "kind": {
          "type": "string",
          "enum": [
           "magic-link",
           "code"
          ]
         }
        },
        "required": [
         "tenantId",
         "destination",
         "channel",
         "kind"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/startPhoneVerification": {
   "post": {
    "operationId": "auth.startPhoneVerification",
    "tags": [
     "auth"
    ],
    "summary": "Texts a six-digit code to a phone number the caller wants to verify.",
    "description": "- **Permission:** The caller's own session, [authenticated recently](#sessions-and-recent-authentication).\n- **Audited as:** not audited; `confirmPhoneVerification` records `auth:phone:verify`.\n- **Errors:** `INVALID_INPUT` when `phone` is not in E.164 format (such as `+14155550100`); `FEATURE_DISABLED` when SMS delivery is not configured; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`; `RATE_LIMITED`.\n\nThe code, sent with the `phone-verify` template, is valid for five minutes and only from the session that requested it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#startphoneverification"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "phone": {
          "type": "string"
         }
        },
        "required": [
         "phone"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/verifyEmail": {
   "post": {
    "operationId": "auth.verifyEmail",
    "tags": [
     "auth"
    ],
    "summary": "Marks an email address verified using the token from a verification email.",
    "description": "- **Permission:** None: public (the token is the proof).\n- **Audited as:** `auth:email:verify`.\n- **Errors:** `INVALID_CHALLENGE` when the token is invalid, already used, or older than 24 hours; `UNAUTHENTICATED` when the account is no longer active; `RATE_LIMITED`.\n\nIt does not sign the person in: send them to your sign-in page afterwards.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#verifyemail"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "token": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "token"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/auth/verifyMfa": {
   "post": {
    "operationId": "auth.verifyMfa",
    "tags": [
     "auth"
    ],
    "summary": "Answers an MFA challenge with a code from the person's authenticator, or an emailed code, and issues the session.",
    "description": "- **Permission:** None: public, with the `challenge` of an `mfaRequired` result.\n- **Audited as:** `auth:session:create` (and `auth:device:trust` with `rememberDevice`); a wrong code is recorded as `auth:signin:fail`.\n- **Errors:** `INVALID_MFA` for a wrong, expired, or already-used code; `MFA_NOT_ENROLLED` (403) when the person has no authenticator and no emailed code was requested; `INVALID_CHALLENGE` when the challenge is invalid or expired; `RATE_LIMITED` (counted per challenge and per person).\n\nAuthenticator codes are six digits. The current 30-second step and one on either side are accepted, and each step only once, so a captured code cannot be replayed. With `rememberDevice: true` the result also carries a `deviceToken` for later sign-ins (see [the sign-in flow](#sign-in-flow-and-mfa-challenges)). Over HTTP it sets the session cookie and, when a device was remembered, the device cookie.\n\n```ts\nconst session = await iam.api.auth.verifyMfa({\n  tenantId,\n  challenge: result.challenge,\n  code: '492039',\n  rememberDevice: true,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/auth#verifymfa"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "challenge": {
          "type": "string"
         },
         "code": {
          "type": "string"
         },
         "rememberDevice": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "challenge",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MfaSessionResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/authorities/create": {
   "post": {
    "operationId": "authorities.create",
    "tags": [
     "authorities"
    ],
    "summary": "Delegates a new grant authority to an identity, bounded by a ceiling and by your own authority chain.",
    "description": "- **Permission:** `iam:authorities:create` on the recipient (`iam/{identityId}`), an active grant authority of your\n  own to delegate from, and a recently authenticated session.\n- **Audited as:** `iam:authorities:create`.\n- **Errors:** `RECENT_AUTH_REQUIRED` when your sign-in is not recent or you call from temporary credentials such as\n  a role session; `IMPERSONATION_RESTRICTED` from a \"view as\" session; `ACCESS_DENIED` when you issue authority to\n  yourself (only root may) or `parentAuthorityId` names an authority that is not yours or is revoked;\n  `GRANT_AUTHORITY_REQUIRED` when you hold no active authority; `INVALID_POLICY`, `INVALID_ACTION`, or\n  `INVALID_RESOURCE_TYPE` when the ceiling does not validate against the catalog; `NOT_FOUND` when the identity is\n  not in this tenant.\n\n`parentAuthorityId` picks which of your authorities the new one hangs under; without it, your root-issued authority\n(root) or your first active delegated authority is used. Give the person a role with the matching `iam:*`\npermissions as well, or the authority lets them grant nothing.\n\n```ts\n// The support lead may hand out support roles, and nothing they grant can exceed tickets and customer reads.\nconst authority = await iam.api.authorities.create(credential, {\n  tenantId,\n  identityId: supportLead.id,\n  ceiling: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['tickets:*', 'customers:read'], resources: ['*'] }],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/authorities#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "ceiling": {
          "$ref": "#/components/schemas/PolicyDocument"
         },
         "parentAuthorityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "ceiling"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/GrantAuthority"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/authorities/revoke": {
   "post": {
    "operationId": "authorities.revoke",
    "tags": [
     "authorities"
    ],
    "summary": "Withdraws a grant authority, so everything issued under it, and under authorities delegated from it, stops granting at the next request.",
    "description": "- **Permission:** `iam:authorities:revoke` on the authority (`iam/{authorityId}`) and a recently authenticated\n  session. You must hold the authority's parent (or be root), and you cannot revoke your own.\n- **Audited as:** `iam:authorities:revoke`.\n- **Errors:** `ACCESS_DENIED` (\"Only superior authority can revoke this grant\") when you do not hold the parent\n  authority; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`; `NOT_FOUND` when the authority is not in this\n  tenant; `INVARIANT_VIOLATION` when the loss of access would break an enforced\n  [access invariant](/docs/reference/api/invariants).\n\nUse it when a delegated administrator changes teams or leaves; offboarding with\n[`identities.offboard`](/docs/reference/api/identities#offboard) does it for you. Removing someone's administrator\nrole alone does not disable the access they provisioned; revoking their authority does. Revocation deletes nothing:\nbindings, roles, and policies issued under the authority remain but grant nothing, and API keys issued under it are\ndenied on every check. There is no way to reinstate a revoked authority; delegate a new one and re-issue what is\nstill needed under it. The result is the authority with `revoked: true`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/authorities#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "authorityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "authorityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "boolean"
            },
            "identityId": {
             "type": "string"
            },
            "ceiling": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "parentAuthorityId": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "revoked",
            "identityId",
            "ceiling",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/accounts": {
   "post": {
    "operationId": "billing.accounts",
    "tags": [
     "billing"
    ],
    "summary": "Lists the billing accounts in the tenant's subtree with this month's billable spend, projection, available credit and latest statement.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n\nOn the root tenant this is every organization: the platform's receivables view. `monthToDateMicros` counts only\nmeters defined above the account (what a statement would bill); `totalMicros` includes the account's own chargeback\nmeters.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#accounts"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AccountOverview"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/anomalies": {
   "post": {
    "operationId": "billing.anomalies",
    "tags": [
     "billing"
    ],
    "summary": "Lists the people, teams and meters whose spend on one day jumped far above their usual daily spend.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `INVALID_INPUT` for a malformed `day`, `baselineDays` outside 3 to 90, `factor` outside 1.1 to 1000, or a\n  negative `minimum`.\n\n`day` defaults to yesterday in the billing time zone. A spike is spend at least `factor` (3) times the average over the\n`baselineDays` (14) before it and at least `minimum` (10 currency units) more; spending with no baseline counts when it\nreaches `minimum` (`factor: null`). The largest increases come first, at most 50. The daily job\n`iam.billing.detectAnomalies()` alerts on them once each (`billing:anomaly`, `spend-anomaly` email).\n\n```ts\nconst { anomalies } = await iam.api.billing.anomalies(admin, { tenantId, factor: 5 });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#anomalies"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/AnomalyOptions"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenantId": {
             "type": "string"
            },
            "anomalies": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/SpendAnomaly"
             }
            }
           },
           "required": [
            "tenantId",
            "anomalies"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/cancelSubscription": {
   "post": {
    "operationId": "billing.cancelSubscription",
    "tags": [
     "billing"
    ],
    "summary": "Cancels a subscription at the end of the month (default) or now.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans, at the month's end only), or\n  a root administrator.\n- **Audited as:** `billing:subscription-cancel`.\n- **Errors:** `ACCESS_DENIED` (403) for an immediate cancellation by anyone but a root administrator;\n  `INVALID_TRANSITION` (409) for an ended subscription.\n\nAt the month's end the subscription keeps running and `resumeSubscription` can undo it. With `atPeriodEnd: false` it\nends now and the unused part of this month's advance fees and seats is credited as invoice items; arrears items bill\nthe part of the month it ran.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#cancelsubscription"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "subscriptionId": {
          "type": "string"
         },
         "atPeriodEnd": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "subscriptionId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "subscription": {
             "$ref": "#/components/schemas/SubscriptionView"
            },
            "invoiceItems": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/InvoiceItemView"
             }
            }
           },
           "required": [
            "subscription",
            "invoiceItems"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/changePlan": {
   "post": {
    "operationId": "billing.changePlan",
    "tags": [
     "billing"
    ],
    "summary": "Moves a subscription to another plan now, keeping its seats and what is left of its trial.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` when both plans are self-serve, or a root\n  administrator.\n- **Audited as:** `billing:subscription-plan-change`.\n- **Errors:** `CONFLICT` (409) when the account already subscribes to the new plan; `INVALID_TRANSITION` (409) for an\n  ended subscription or an archived plan; `INVALID_INPUT` for the same plan.\n\nThe old subscription ends and a new one starts. The old plan's unused advance charges are credited and the new plan's\ncharges for the rest of the month added, both as invoice items for the next invoice (`invoiceItems`).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#changeplan"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "subscriptionId": {
          "type": "string"
         },
         "plan": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "subscriptionId",
         "plan"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "subscription": {
             "$ref": "#/components/schemas/SubscriptionView"
            },
            "invoiceItems": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/InvoiceItemView"
             }
            }
           },
           "required": [
            "subscription",
            "invoiceItems"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/check": {
   "post": {
    "operationId": "billing.check",
    "tags": [
     "billing"
    ],
    "summary": "Reports whether the caller's own usage is within every enforced budget that covers it.",
    "description": "- **Permission:** The caller's own session; the tenant must be the caller's tenant or one below it.\n- **Audited as:** not audited.\n- **Errors:** `ACCESS_DENIED` (403) for a tenant outside the caller's.\n\nCovering budgets are enforced tenant budgets on the tenant or an ancestor, and the caller's own, their teams' (with\nparent teams) and their department's (with the departments above it); `meter` narrows to budgets that count it.\n`blockedBy` names the first spent budget. Statuses may lag recorded usage by up to 30 seconds. Server code checks any\nidentity with `iam.billing.check`.\n\n```ts\nconst verdict = await client.billing.check({ tenantId, meter: 'api-calls' });\nif (!verdict.allowed) showBudgetBanner(verdict.blockedBy);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#check"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "meter": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SpendCheck"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/closePeriod": {
   "post": {
    "operationId": "billing.closePeriod",
    "tags": [
     "billing"
    ],
    "summary": "Issues statements for a month that has ended, for every billing account or only accountId's.",
    "description": "- **Permission:** Root administrators only, called on the root tenant (`iam:billing:manage` on `iam/billing/periods`).\n- **Audited as:** `iam:billing:manage`; each statement as `billing:statement`.\n- **Errors:** `INVALID_INPUT` for the current or a future month, or when called on another tenant.\n\n`period` defaults to last month. Each invoice bills the month's usage of platform meters, the subscriptions' fees\nand seats (the month itself for arrears items, the next month for advance ones), and pending invoice items. Accounts\nalready invoiced for the month and accounts with nothing to bill are skipped (`skipped.existing`, `skipped.empty`), so\nthe job is safe to repeat. Coupons apply after the contract discount, credit earliest expiry first; invoices are\nemailed (`billing-statement`) to the profile's billing emails or the owners, and raw usage events past their retention\nare deleted (`sweptUsage`). With `draft: true` (or the option `billing.autoFinalize: false`) invoices are kept as\ndrafts, refreshed on every run, and listed in `drafted`; finalize them with `finalizeInvoice`. Schedulers call\n`iam.billing.closePeriod()` instead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#closeperiod"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         },
         "accountId": {
          "type": "string"
         },
         "draft": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ClosePeriodResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createBudget": {
   "post": {
    "operationId": "billing.createBudget",
    "tags": [
     "billing"
    ],
    "summary": "Creates a spend budget for the tenant, a tenant below it, a team, a department or a person, per month, quarter or year.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/budgets`.\n- **Audited as:** `billing:budget-create`.\n- **Errors:** `CONFLICT` (409) for a name already used (case-insensitive); `LIMIT_EXCEEDED` (409) past 200 budgets;\n  `NOT_FOUND` for a subject outside the tenant; `INVALID_INPUT` for an amount of 0, a malformed meter key, more than 10\n  thresholds or notification addresses.\n\n`amount` is in currency units. `thresholds` (default 50, 80, 100 percent) and `forecastAlerts` (default on) drive the\nalerts that `iam.billing.checkBudgets()` sends once per window to the owners, the subject (the person, the team's\nmaintainers or the department head) and `notify.emails`. `enforce` makes covered usage fail once the budget is spent.\nThe result is the budget with its current standing.\n\n```ts\nawait iam.api.billing.createBudget(admin, {\n  tenantId,\n  name: 'Platform team monthly',\n  subjectType: 'team',\n  subjectId: platformTeamId,\n  amount: 600,\n  notify: { emails: ['finance@acme.test'] },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createbudget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/BillingBudgetInput"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/BillingBudgetView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createCoupon": {
   "post": {
    "operationId": "billing.createCoupon",
    "tags": [
     "billing"
    ],
    "summary": "Creates a coupon accounts can redeem by code.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/coupons`), called on the root tenant.\n- **Audited as:** `billing:coupon`.\n- **Errors:** `CONFLICT` (409) for a code already used; `INVALID_INPUT` for a code that is not 3 to 32 letters, digits,\n  `-` or `_`, both or neither of `percentOff` and `amountOff`, a percentage outside 0 to 100, `durationInMonths`\n  without `duration: 'repeating'`, or a `redeemBy` in the past.\n\nCodes are stored in upper case. `duration` is `once` (default: the next invoice), `repeating` (invoices for\n`durationInMonths` months from the month of redemption) or `forever`. `maxRedemptions` caps how many accounts may\nredeem it.\n\n```ts\nawait iam.api.billing.createCoupon(root, {\n  tenantId: rootTenantId,\n  code: 'LAUNCH20',\n  percentOff: 20,\n  duration: 'repeating',\n  durationInMonths: 3,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createcoupon"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "code": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "percentOff": {
          "type": "number"
         },
         "amountOff": {
          "type": "number"
         },
         "duration": {
          "type": "string",
          "enum": [
           "once",
           "repeating",
           "forever"
          ]
         },
         "durationInMonths": {
          "type": "number"
         },
         "maxRedemptions": {
          "type": "number"
         },
         "redeemBy": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CouponView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createCreditNote": {
   "post": {
    "operationId": "billing.createCreditNote",
    "tags": [
     "billing"
    ],
    "summary": "Issues a credit note against a finalized, paid or uncollectible invoice.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`).\n- **Audited as:** `billing:credit-note`.\n- **Errors:** `INVALID_INPUT` for an amount above what is left to credit, an unknown `reason`, or a non-boolean\n  `refund`; `INVALID_TRANSITION` (409) for a draft or void invoice.\n\n`amount` defaults to everything not yet credited. The note first reduces the amount due; the rest (a part already\npaid) becomes account credit, or with `refund: true` is recorded as refunded outside Better IAM. `applied` shows the\nsplit. Notes are numbered `{invoice}-CN-01`, `-CN-02`, ...; `reason` is `duplicate`, `fraudulent`, `order_change`,\n`product_unsatisfactory` or `other` (default), with an optional `memo`. An invoice the notes and payments cover is paid.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createcreditnote"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         },
         "amount": {
          "type": "number"
         },
         "reason": {
          "type": "string",
          "enum": [
           "duplicate",
           "fraudulent",
           "order_change",
           "product_unsatisfactory",
           "other"
          ]
         },
         "memo": {
          "type": "string"
         },
         "refund": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "statement": {
             "$ref": "#/components/schemas/StatementSummary"
            },
            "creditNote": {
             "$ref": "#/components/schemas/CreditNoteView"
            }
           },
           "required": [
            "statement",
            "creditNote"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createInvoiceItem": {
   "post": {
    "operationId": "billing.createInvoiceItem",
    "tags": [
     "billing"
    ],
    "summary": "Adds a one-off charge, or with a negative amount a credit, to the billing account's next invoice.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/invoice-items`), called on the account.\n- **Audited as:** `billing:invoice-item`.\n- **Errors:** `INVALID_INPUT` when the tenant is not a billing account, for an empty description, an amount beyond one\n  billion, a quantity of 0, or more than 20 metadata entries; `BILLING_PERIOD_CLOSED` (409) for a `period` already\n  invoiced.\n\n`amount` is per unit in currency units and `quantity` defaults to 1; the item's total is rounded to the cent. `period`\nbills it on the invoice for that month instead of the next one. When credit items exceed an invoice's charges the\ninvoice totals 0 and the rest becomes account credit.\n\n```ts\nawait iam.api.billing.createInvoiceItem(root, {\n  tenantId: acmeId,\n  description: 'Onboarding workshop',\n  amount: 500,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createinvoiceitem"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "amount": {
          "type": "number"
         },
         "quantity": {
          "type": "number"
         },
         "period": {
          "type": "string"
         },
         "metadata": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId",
         "description",
         "amount"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/InvoiceItemView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createMeter": {
   "post": {
    "operationId": "billing.createMeter",
    "tags": [
     "billing"
    ],
    "summary": "Defines a usage meter: a platform meter on the root tenant, a chargeback meter for the tenant's subtree elsewhere.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.\n- **Audited as:** `billing:meter-create`.\n- **Errors:** `CONFLICT` (409) when a meter with the key already reaches the tenant; `LIMIT_EXCEEDED` (409) past 100\n  meters; `INVALID_INPUT` for a malformed key or a `unique` reported meter.\n\nKeys are 1 to 64 lowercase letters, digits, dots, underscores or hyphens, starting with a letter. `aggregation` is\n`sum` (default) or `unique` (distinct people and agents per month); `pricing` is `rate-card` (default) or `reported`\n(each event carries its cost). Neither can change later.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createmeter"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "unit": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "aggregation": {
          "type": "string",
          "enum": [
           "sum",
           "unique"
          ]
         },
         "pricing": {
          "type": "string",
          "enum": [
           "rate-card",
           "reported"
          ]
         }
        },
        "required": [
         "tenantId",
         "key",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MeterView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/createPlan": {
   "post": {
    "operationId": "billing.createPlan",
    "tags": [
     "billing"
    ],
    "summary": "Defines a plan on the platform (root) tenant.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/plans`), called on the root tenant.\n- **Audited as:** `billing:plan`.\n- **Errors:** `CONFLICT` (409) for a key already used; `LIMIT_EXCEEDED` (409) past 100 plans; `INVALID_INPUT` for a\n  malformed key, 0 or more than 20 items, duplicate item ids, two items pricing one meter, or a malformed price.\n\n`items` are `fee` (`amount` per month), `seat` (`unitAmount` per seat and month, `includedSeats`), both billed in\n`advance` (default) or `arrears`, and `usage` (`meter` with a `price` that replaces the rate card for subscribers).\n`trialDays` (1 to 365) starts subscriptions with a free trial; `selfServe` lets account billing managers subscribe.\n\n```ts\nawait iam.api.billing.createPlan(root, {\n  tenantId: rootTenantId,\n  key: 'team',\n  name: 'Team',\n  selfServe: true,\n  items: [\n    { id: 'platform', kind: 'fee', name: 'Platform fee', amount: 99 },\n    { id: 'seats', kind: 'seat', name: 'Seats', unitAmount: 12, includedSeats: 3 },\n  ],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#createplan"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "items": {
          "type": "array",
          "items": {}
         },
         "description": {
          "type": "string"
         },
         "trialDays": {
          "type": "number"
         },
         "selfServe": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "key",
         "name",
         "items"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PlanView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/deactivateCoupon": {
   "post": {
    "operationId": "billing.deactivateCoupon",
    "tags": [
     "billing"
    ],
    "summary": "Stops a coupon (by code) from being redeemed; accounts that redeemed it keep their discount.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/coupons`), called on the root tenant.\n- **Audited as:** `billing:coupon-deactivate`.\n- **Errors:** `NOT_FOUND` for an unknown code.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#deactivatecoupon"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "code": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CouponView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/deleteBudget": {
   "post": {
    "operationId": "billing.deleteBudget",
    "tags": [
     "billing"
    ],
    "summary": "Deletes a budget and its alert history.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/budgets`.\n- **Audited as:** `billing:budget-delete`.\n- **Errors:** `NOT_FOUND` for a budget of another tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#deletebudget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "budgetId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "budgetId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/deleteInvoiceItem": {
   "post": {
    "operationId": "billing.deleteInvoiceItem",
    "tags": [
     "billing"
    ],
    "summary": "Deletes a pending invoice item.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/invoice-items`), called on the account.\n- **Audited as:** `billing:invoice-item-delete`.\n- **Errors:** `INVALID_TRANSITION` (409) for an item already on a finalized invoice (issue a credit note instead).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#deleteinvoiceitem"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "itemId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "itemId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/deleteMeter": {
   "post": {
    "operationId": "billing.deleteMeter",
    "tags": [
     "billing"
    ],
    "summary": "Deletes a meter that has never recorded usage, with its prices.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.\n- **Audited as:** `billing:meter-delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) once the meter has recorded usage (archive it with `updateMeter` instead);\n  `NOT_FOUND` when the tenant does not define the key.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#deletemeter"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            },
            "removedPrices": {
             "type": "number"
            }
           },
           "required": [
            "success",
            "removedPrices"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/deleteProfile": {
   "post": {
    "operationId": "billing.deleteProfile",
    "tags": [
     "billing"
    ],
    "summary": "Removes a billing profile, so the tenant's usage rolls into its parent's account again.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/profile`; below an organization, called from an ancestor with\n  `targetTenantId`.\n- **Audited as:** `billing:profile-delete`.\n- **Errors:** `NOT_FOUND` without a profile; `ACCESS_DENIED` (403) when a tenant below an organization removes its own.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#deleteprofile"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "targetTenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/departmentSpend": {
   "post": {
    "operationId": "billing.departmentSpend",
    "tags": [
     "billing"
    ],
    "summary": "Reports a department's spend, with the departments below it, grouped by identity by default.",
    "description": "- **Permission:** The department's head (or the head of a department above it), or `iam:billing:read`.\n- **Audited as:** `iam:billing:read`, with `metadata.via` `department-head` or `permission`.\n- **Errors:** `ACCESS_DENIED` (403) for anyone else; `NOT_FOUND` for a department outside the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#departmentspend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         },
         "groupBy": {
          "anyOf": [
           {
            "type": "string",
            "const": "tenant"
           },
           {
            "type": "string",
            "const": "agent"
           },
           {
            "type": "string",
            "const": "identity"
           },
           {
            "type": "string",
            "const": "team"
           },
           {
            "type": "string",
            "const": "department"
           },
           {
            "type": "string",
            "const": "meter"
           },
           {
            "type": "string",
            "const": "day"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "departmentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SpendReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/exportSpend": {
   "post": {
    "operationId": "billing.exportSpend",
    "tags": [
     "billing"
    ],
    "summary": "Returns a spend report as a CSV file: one row per group with the amount, share, events and a column per meter's quantity.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** as `spend`.\n\nIt takes the same input as `spend` and returns `filename`, `contentType` and `body` (RFC 4180, CRLF line endings, a\nfinal total row). Cells that a spreadsheet would run as a formula are prefixed with a quote.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#exportspend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/SpendQuery"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CsvExport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/exportStatement": {
   "post": {
    "operationId": "billing.exportStatement",
    "tags": [
     "billing"
    ],
    "summary": "Returns a statement as a CSV file: its lines, credit and total, then the breakdown by project, team, department and person.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`; the statement's account must be the tenant or below it.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` for a statement outside the tenant's subtree.\n\nThe file is named after the statement number. Department rows carry their cost center.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#exportstatement"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CsvExport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/finalizeInvoice": {
   "post": {
    "operationId": "billing.finalizeInvoice",
    "tags": [
     "billing"
    ],
    "summary": "Finalizes a draft invoice: recomputes it with the latest usage and invoice items, numbers it, seals it and emails it.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account\n  or the root tenant.\n- **Audited as:** `billing:statement-finalize` and `billing:statement`.\n- **Errors:** `INVALID_TRANSITION` (409) for an invoice that is not a draft, or a draft with nothing left to bill.\n\nThe invoice then consumes its credit, marks its invoice items `invoiced`, its subscriptions' advance months billed, and\nits coupons used. An invoice with nothing to pay is `paid` on issue.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#finalizeinvoice"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/StatementSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/getProfile": {
   "post": {
    "operationId": "billing.getProfile",
    "tags": [
     "billing"
    ],
    "summary": "Returns the billing profile of the tenant (or of targetTenantId below it) and the account that pays for it.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/profile`.\n- **Audited as:** `iam:billing:read`.\n\n`account.inherited` is true when an ancestor pays; `profile` is null without a profile of its own.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#getprofile"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "targetTenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ProfileView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/getStatement": {
   "post": {
    "operationId": "billing.getStatement",
    "tags": [
     "billing"
    ],
    "summary": "Returns one statement with its lines, credit, breakdown and bill-to details, re-checking its content hash.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`; the statement's account must be the tenant or below it.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` for a statement outside the tenant's subtree.\n\n`verified` is false when the stored content no longer matches the hash computed at issue. `overdue` is true for a\nfinalized statement past its due date.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#getstatement"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "total": {
             "type": "number"
            },
            "overdue": {
             "type": "boolean"
            },
            "verified": {
             "type": "boolean"
            },
            "number": {
             "type": "string"
            },
            "status": {
             "$ref": "#/components/schemas/InvoiceStatus"
            },
            "issuedAt": {
             "type": "number"
            },
            "dueAt": {
             "type": "number"
            },
            "hash": {
             "type": "string"
            },
            "payments": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/InvoicePayment"
             }
            },
            "amountPaidMicros": {
             "type": "number"
            },
            "creditNotesMicros": {
             "type": "number"
            },
            "invoiceItemIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "advanceBilled": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "subscriptionId": {
                "type": "string"
               },
               "period": {
                "type": "string"
               }
              },
              "required": [
               "subscriptionId",
               "period"
              ],
              "additionalProperties": false
             }
            },
            "carryForward": {
             "type": "object",
             "properties": {
              "creditId": {
               "type": "string"
              },
              "amountMicros": {
               "type": "number"
              }
             },
             "required": [
              "creditId",
              "amountMicros"
             ],
             "additionalProperties": false
            },
            "reminders": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "days": {
                "type": "number"
               },
               "at": {
                "type": "number"
               },
               "recipients": {
                "type": "number"
               }
              },
              "required": [
               "days",
               "at",
               "recipients"
              ],
              "additionalProperties": false
             }
            },
            "paidAt": {
             "type": "number"
            },
            "paidBy": {
             "type": "string"
            },
            "paymentReference": {
             "type": "string"
            },
            "voidedAt": {
             "type": "number"
            },
            "voidedBy": {
             "type": "string"
            },
            "voidReason": {
             "type": "string"
            },
            "markedUncollectibleAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            },
            "period": {
             "type": "string"
            },
            "currency": {
             "type": "string"
            },
            "periodStart": {
             "type": "number"
            },
            "periodEnd": {
             "type": "number"
            },
            "lines": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/StatementLine"
             }
            },
            "billingReason": {
             "type": "string",
             "enum": [
              "period",
              "subscription",
              "manual"
             ]
            },
            "subtotalMicros": {
             "type": "number"
            },
            "coupons": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "discountId": {
                "type": "string"
               },
               "code": {
                "type": "string"
               },
               "name": {
                "type": "string"
               },
               "amountMicros": {
                "type": "number"
               }
              },
              "required": [
               "discountId",
               "code",
               "name",
               "amountMicros"
              ],
              "additionalProperties": false
             }
            },
            "discount": {
             "type": "object",
             "properties": {
              "percent": {
               "type": "number"
              },
              "amountMicros": {
               "type": "number"
              }
             },
             "required": [
              "percent",
              "amountMicros"
             ],
             "additionalProperties": false
            },
            "commitment": {
             "type": "object",
             "properties": {
              "minimumMicros": {
               "type": "number"
              },
              "trueUpMicros": {
               "type": "number"
              }
             },
             "required": [
              "minimumMicros",
              "trueUpMicros"
             ],
             "additionalProperties": false
            },
            "creditsMicros": {
             "type": "number"
            },
            "creditsApplied": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "creditId": {
                "type": "string"
               },
               "amountMicros": {
                "type": "number"
               }
              },
              "required": [
               "creditId",
               "amountMicros"
              ],
              "additionalProperties": false
             }
            },
            "tax": {
             "type": "object",
             "properties": {
              "label": {
               "type": "string"
              },
              "ratePercent": {
               "type": "number"
              },
              "amountMicros": {
               "type": "number"
              }
             },
             "required": [
              "label",
              "ratePercent",
              "amountMicros"
             ],
             "additionalProperties": false
            },
            "totalMicros": {
             "type": "number"
            },
            "breakdown": {
             "type": "object",
             "properties": {
              "tenants": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/StatementAllocation"
               }
              },
              "teams": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/StatementAllocation"
               }
              },
              "departments": {
               "type": "array",
               "items": {
                "allOf": [
                 {
                  "$ref": "#/components/schemas/StatementAllocation"
                 },
                 {
                  "type": "object",
                  "properties": {
                   "costCenter": {
                    "type": "string"
                   }
                  },
                  "additionalProperties": false
                 }
                ]
               }
              },
              "identities": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/StatementAllocation"
               }
              }
             },
             "required": [
              "tenants",
              "teams",
              "departments",
              "identities"
             ],
             "additionalProperties": false
            },
            "billTo": {
             "type": "object",
             "properties": {
              "name": {
               "type": "string"
              },
              "companyName": {
               "type": "string"
              },
              "taxId": {
               "type": "string"
              },
              "address": {
               "type": "string"
              },
              "purchaseOrder": {
               "type": "string"
              },
              "costCenter": {
               "type": "string"
              },
              "emails": {
               "type": "array",
               "items": {
                "type": "string"
               }
              }
             },
             "required": [
              "name",
              "emails"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "total",
            "overdue",
            "verified",
            "number",
            "status",
            "issuedAt",
            "dueAt",
            "hash",
            "id",
            "tenantId",
            "period",
            "currency",
            "periodStart",
            "periodEnd",
            "lines",
            "subtotalMicros",
            "creditsMicros",
            "creditsApplied",
            "totalMicros",
            "breakdown",
            "billTo"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/getTerms": {
   "post": {
    "operationId": "billing.getTerms",
    "tags": [
     "billing"
    ],
    "summary": "Returns the contract terms of the tenant's billing account: discount, minimum monthly commitment and tax.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/terms`.\n- **Audited as:** `iam:billing:read`.\n\nWhen an ancestor pays for the tenant the result only says `inherited: true`; without terms it lists none.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#getterms"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TermsView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/grantCredit": {
   "post": {
    "operationId": "billing.grantCredit",
    "tags": [
     "billing"
    ],
    "summary": "Grants credit to a billing account, which its statements draw on, earliest expiry first.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/credits`).\n- **Audited as:** `billing:credit-grant`.\n- **Errors:** `INVALID_INPUT` when the tenant is not a billing account (an organization or a tenant with a profile), for\n  an amount of 0, or an `expiresAt` in the past.\n\n```ts\nawait iam.api.billing.grantCredit(root, { tenantId: acmeId, amount: 100, reason: 'Launch promotion' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#grantcredit"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "amount": {
          "type": "number"
         },
         "reason": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "amount",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CreditView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listBudgets": {
   "post": {
    "operationId": "billing.listBudgets",
    "tags": [
     "billing"
    ],
    "summary": "Lists the tenant's budgets with their spend, projection and the thresholds reached in the current window.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/budgets`.\n- **Audited as:** `iam:billing:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listbudgets"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/BillingBudgetView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listCoupons": {
   "post": {
    "operationId": "billing.listCoupons",
    "tags": [
     "billing"
    ],
    "summary": "Lists the platform's coupons, newest first, with their redemptions.",
    "description": "- **Permission:** Root administrators only (`iam:billing:read` on `iam/billing/coupons`), called on the root tenant.\n- **Audited as:** `iam:billing:read`.\n\n`active` is false for deactivated coupons, those past `redeemBy`, and those out of redemptions.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listcoupons"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/CouponView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listCreditNotes": {
   "post": {
    "operationId": "billing.listCreditNotes",
    "tags": [
     "billing"
    ],
    "summary": "Lists the credit notes of the billing accounts in the tenant's subtree, or of one statementId, newest first.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`.\n- **Audited as:** `iam:billing:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listcreditnotes"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/CreditNoteView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listCredits": {
   "post": {
    "operationId": "billing.listCredits",
    "tags": [
     "billing"
    ],
    "summary": "Lists the credit of the tenant's billing account with the available balance.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/credits`.\n- **Audited as:** `iam:billing:read`.\n\nWhen an ancestor pays for the tenant the list is empty and `inherited` is true.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listcredits"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "accountId": {
             "type": "string"
            },
            "inherited": {
             "type": "boolean"
            },
            "balanceMicros": {
             "type": "number"
            },
            "balance": {
             "type": "number"
            },
            "credits": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/CreditView"
             }
            }
           },
           "required": [
            "accountId",
            "inherited",
            "balanceMicros",
            "balance",
            "credits"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listDiscounts": {
   "post": {
    "operationId": "billing.listDiscounts",
    "tags": [
     "billing"
    ],
    "summary": "Lists the discounts (redeemed coupons) of the billing accounts in the tenant's subtree.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/discounts`.\n- **Audited as:** `iam:billing:read`.\n\n`appliedInvoices` counts the invoices a discount reduced; `active` says whether it applies to this month's invoice.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listdiscounts"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DiscountView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listInvoiceItems": {
   "post": {
    "operationId": "billing.listInvoiceItems",
    "tags": [
     "billing"
    ],
    "summary": "Lists invoice items of the billing accounts in the tenant's subtree, newest first, optionally by status.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/invoice-items`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `INVALID_INPUT` for a `status` other than `pending` or `invoiced`.\n\n`pending` items wait for the account's next invoice (or the one for their `period`); `invoiced` items carry the\n`statementId` that billed them. `source: 'proration'` items come from subscription changes.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listinvoiceitems"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "invoiced"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/InvoiceItemView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listMeters": {
   "post": {
    "operationId": "billing.listMeters",
    "tags": [
     "billing"
    ],
    "summary": "Lists the meters that reach the tenant with the price that applies to its billing account this month.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n\n`scope` is `platform` for root meters; `inherited` marks meters an ancestor defines.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listmeters"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/MeterView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listPlans": {
   "post": {
    "operationId": "billing.listPlans",
    "tags": [
     "billing"
    ],
    "summary": "Lists the platform's plans with their fees, seats and meter prices.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/plans`.\n- **Audited as:** `iam:billing:read`.\n\nTenants see plans that are not archived. Root administrators on the root tenant also get `subscribers` (live\nsubscriptions) and, with `includeArchived`, archived plans. Amounts are in currency units.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listplans"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "includeArchived": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PlanView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listPrices": {
   "post": {
    "operationId": "billing.listPrices",
    "tags": [
     "billing"
    ],
    "summary": "Returns a meter's rate card as the tenant may see it, and the entry that prices its account this month.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/meters/{key}`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` when no meter with the key reaches the tenant.\n\nEntries for the tenant, its ancestors (list prices) and tenants below it are listed, newest first; negotiated prices\nfor other organizations are not.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listprices"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "meter": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "meter"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "effective": {
             "$ref": "#/components/schemas/PriceView"
            },
            "meter": {
             "$ref": "#/components/schemas/MeterView"
            },
            "entries": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/PriceView"
             }
            }
           },
           "required": [
            "meter",
            "entries"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listStatements": {
   "post": {
    "operationId": "billing.listStatements",
    "tags": [
     "billing"
    ],
    "summary": "Lists the statements of the billing accounts in the tenant's subtree (all of them for the root), newest month first.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`.\n- **Audited as:** `iam:billing:read`.\n\nFilter by `status` (`finalized`, `paid`, `void`) or `period`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#liststatements"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "draft",
           "finalized",
           "paid",
           "void",
           "uncollectible"
          ]
         },
         "period": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/StatementSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listSubscriptions": {
   "post": {
    "operationId": "billing.listSubscriptions",
    "tags": [
     "billing"
    ],
    "summary": "Lists the subscriptions of the billing accounts in the tenant's subtree, newest first.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/subscriptions`.\n- **Audited as:** `iam:billing:read`.\n\nEnded subscriptions are left out unless `includeEnded`. `status` is `trialing`, `active` or `ended`; `billedAdvance`\nlists the months already billed in advance.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listsubscriptions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "includeEnded": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/SubscriptionView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/listUsage": {
   "post": {
    "operationId": "billing.listUsage",
    "tags": [
     "billing"
    ],
    "summary": "Lists raw usage events recorded in the tenant for a month, newest first.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n\nOnly the tenant itself (not its subtree), at most `limit` (1 to 500, default 100) events, optionally for one `meter`\nor identity. Events are kept `usageRetentionDays` after their month; reports use daily roll-ups and outlive them.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#listusage"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         },
         "meter": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenantId": {
             "type": "string"
            },
            "period": {
             "type": "string"
            },
            "events": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "idempotencyKey": {
                "type": "string"
               },
               "meterId": {
                "type": "string"
               },
               "meter": {
                "type": "string"
               },
               "quantity": {
                "type": "number"
               },
               "costMicros": {
                "type": "number"
               },
               "identityId": {
                "type": "string"
               },
               "agentId": {
                "type": "string"
               },
               "teamIds": {
                "type": "array",
                "items": {
                 "type": "string"
                }
               },
               "departmentId": {
                "type": "string"
               },
               "tags": {
                "type": "object",
                "properties": {},
                "additionalProperties": {
                 "type": "string"
                }
               },
               "occurredAt": {
                "type": "number"
               },
               "period": {
                "type": "string"
               },
               "day": {
                "type": "string"
               },
               "recordedAt": {
                "type": "number"
               },
               "recordedBy": {
                "type": "string"
               },
               "sourceId": {
                "type": "string"
               },
               "id": {
                "type": "string"
               },
               "tenantId": {
                "type": "string"
               }
              },
              "required": [
               "meterId",
               "meter",
               "quantity",
               "teamIds",
               "occurredAt",
               "period",
               "day",
               "recordedAt",
               "recordedBy",
               "id",
               "tenantId"
              ],
              "additionalProperties": false
             }
            }
           },
           "required": [
            "tenantId",
            "period",
            "events"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/markPaid": {
   "post": {
    "operationId": "billing.markPaid",
    "tags": [
     "billing"
    ],
    "summary": "Marks a finalized or uncollectible statement paid by recording a manual payment of the amount due, with an optional reference.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account\n  or the root tenant.\n- **Audited as:** `billing:statement-paid`.\n- **Errors:** `INVALID_TRANSITION` (409) for a draft, paid or void statement.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#markpaid"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         },
         "reference": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/StatementSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/markUncollectible": {
   "post": {
    "operationId": "billing.markUncollectible",
    "tags": [
     "billing"
    ],
    "summary": "Writes a finalized invoice off as uncollectible; a later payment still settles it.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`).\n- **Audited as:** `billing:statement-uncollectible`.\n- **Errors:** `INVALID_TRANSITION` (409) for an invoice that is not finalized.\n\nUncollectible invoices get no payment reminders.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#markuncollectible"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/StatementSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/mySpend": {
   "post": {
    "operationId": "billing.mySpend",
    "tags": [
     "billing"
    ],
    "summary": "Reports the caller's own spend: their usage and that of the agents they sponsor, with the budgets set on them.",
    "description": "- **Permission:** The caller's own session in their own tenant.\n- **Audited as:** not audited.\n- **Errors:** `ACCESS_DENIED` (403) for another tenant; `INVALID_INPUT` for a `groupBy` other than `meter`, `day`,\n  `agent`, `tenant` or `tag:{name}`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#myspend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         },
         "groupBy": {
          "anyOf": [
           {
            "type": "string",
            "const": "tenant"
           },
           {
            "type": "string",
            "const": "agent"
           },
           {
            "type": "string",
            "const": "identity"
           },
           {
            "type": "string",
            "const": "team"
           },
           {
            "type": "string",
            "const": "department"
           },
           {
            "type": "string",
            "const": "meter"
           },
           {
            "type": "string",
            "const": "day"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/SpendReport"
            },
            {
             "type": "object",
             "properties": {
              "budgets": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/BudgetStatus"
               }
              }
             },
             "required": [
              "budgets"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/previewStatement": {
   "post": {
    "operationId": "billing.previewStatement",
    "tags": [
     "billing"
    ],
    "summary": "Builds the statement a billing account would receive for a month (the current one so far by default), without issuing it.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `INVALID_INPUT` when the tenant is not a billing account.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#previewstatement"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/StatementDraft"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/quote": {
   "post": {
    "operationId": "billing.quote",
    "tags": [
     "billing"
    ],
    "summary": "Prices a quantity of a meter as a month total for the tenant's billing account.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/meters/{key}`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` for an unknown meter; `INVALID_INPUT` for a reported meter or a negative quantity.\n\nTiers and free units apply as they would to the account's month total; `unpriced` is true without a price.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#quote"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "meter": {
          "type": "string"
         },
         "quantity": {
          "type": "number"
         },
         "period": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "meter",
         "quantity"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "anyOf": [
            {
             "type": "object",
             "properties": {
              "price": {
               "$ref": "#/components/schemas/PriceView"
              },
              "meter": {
               "type": "string"
              },
              "quantity": {
               "type": "number"
              },
              "period": {
               "type": "string"
              },
              "currency": {
               "type": "string"
              },
              "amountMicros": {
               "type": "number"
              },
              "amount": {
               "type": "number"
              }
             },
             "required": [
              "price",
              "meter",
              "quantity",
              "period",
              "currency",
              "amountMicros",
              "amount"
             ],
             "additionalProperties": false
            },
            {
             "type": "object",
             "properties": {
              "unpriced": {
               "type": "boolean",
               "const": true
              },
              "meter": {
               "type": "string"
              },
              "quantity": {
               "type": "number"
              },
              "period": {
               "type": "string"
              },
              "currency": {
               "type": "string"
              },
              "amountMicros": {
               "type": "number"
              },
              "amount": {
               "type": "number"
              }
             },
             "required": [
              "unpriced",
              "meter",
              "quantity",
              "period",
              "currency",
              "amountMicros",
              "amount"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/record": {
   "post": {
    "operationId": "billing.record",
    "tags": [
     "billing"
    ],
    "summary": "Records usage of a meter in the tenant, attributed to an identity, their teams and department, with optional tags.",
    "description": "- **Permission:** `iam:billing:record` on `iam/billing/meters/{key}`.\n- **Audited as:** `iam:billing:record`.\n- **Errors:** `NOT_FOUND` for an unknown meter or an identity outside the tenant and its ancestors; `METER_ARCHIVED`\n  (409); `BILLING_PERIOD_CLOSED` (409) for a month already invoiced; `SPEND_LIMIT_REACHED` (402) with\n  `enforceBudgets` under a spent enforced budget; `CONFLICT` (409) for an idempotency key used on another meter;\n  `INVALID_INPUT` for a negative quantity, `cost` on a rate-card meter or none on a reported one, or `occurredAt` more\n  than five minutes ahead or a year back.\n\n`idempotencyKey` makes retries safe: a repeat returns the first receipt with `duplicate: true`. `teamId` attributes the\nusage to one team instead of the person's own. Server code records without an audit event per call through\n`iam.billing.record`, the usual choice for metering.\n\n```ts\nawait client.billing.record({\n  tenantId,\n  meter: 'api-calls',\n  quantity: 1,\n  identityId,\n  tags: { endpoint: 'search' },\n  idempotencyKey: requestId,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#record"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/UsageInput"
         },
         {
          "type": "object",
          "properties": {
           "enforceBudgets": {
            "type": "boolean"
           }
          },
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/UsageReceipt"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/recordMany": {
   "post": {
    "operationId": "billing.recordMany",
    "tags": [
     "billing"
    ],
    "summary": "Records up to 100 usage events in one transaction, all or nothing.",
    "description": "- **Permission:** `iam:billing:record` on `iam/billing` in the call's tenant.\n- **Audited as:** `iam:billing:record`.\n- **Errors:** as `record`; `INVALID_INPUT` for an event outside the tenant's subtree or more than 100 events.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#recordmany"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "events": {
          "allOf": [
           {
            "type": "object",
            "properties": {
             "identityId": {
              "type": "string",
              "description": "The person, service account or agent that used it (an identity of the tenant or an ancestor)."
             },
             "meter": {
              "type": "string"
             },
             "quantity": {
              "type": "number",
              "description": "Default 1."
             },
             "cost": {
              "type": "number",
              "description": "Reported meters only: the event's cost in currency units."
             },
             "teamId": {
              "type": "string",
              "description": "Attribute to this team instead of the person's own teams."
             },
             "tags": {
              "type": "object",
              "properties": {},
              "additionalProperties": {
               "type": "string"
              },
              "description": "Up to 10 labels (`project`, `environment`, `feature`) reports can group by."
             },
             "occurredAt": {
              "type": "number",
              "description": "When it happened (default now); at most five minutes ahead, and never in a period already invoiced."
             },
             "idempotencyKey": {
              "type": "string",
              "description": "Makes retries safe: a repeated key returns the first receipt and records nothing."
             }
            },
            "required": [
             "meter"
            ],
            "additionalProperties": false
           },
           {
            "type": "array",
            "items": {
             "type": "object",
             "properties": {
              "tenantId": {
               "type": "string"
              }
             },
             "additionalProperties": false
            }
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "events"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "receipts": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/UsageReceipt"
             }
            }
           },
           "required": [
            "receipts"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/recordPayment": {
   "post": {
    "operationId": "billing.recordPayment",
    "tags": [
     "billing"
    ],
    "summary": "Records a payment against a finalized or uncollectible invoice.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account\n  or the root tenant.\n- **Audited as:** `billing:payment`.\n- **Errors:** `INVALID_TRANSITION` (409) for a draft, paid or void invoice; `INVALID_INPUT` for an amount of 0 or a\n  `receivedAt` in the future.\n\n`amount` (currency units) defaults to the amount due; `method` (default `manual`) and `reference` describe it. A\npartial payment leaves the rest due; once payments and credit notes cover the invoice it is `paid`. A payment above\nthe amount due keeps the excess as account credit (`payment.overpaymentMicros`, `payment.creditId`). Payment\nprocessors report payments through `iam.billing.recordPayment({ statementId | number, amount, idempotencyKey })`\ninstead, where the idempotency key makes webhook redelivery safe.\n\n```ts\nawait iam.api.billing.recordPayment(root, {\n  tenantId: acmeId,\n  statementId,\n  amount: 300,\n  method: 'bank_transfer',\n  reference: 'wire-88213',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#recordpayment"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         },
         "amount": {
          "type": "number"
         },
         "method": {
          "type": "string"
         },
         "reference": {
          "type": "string"
         },
         "receivedAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "statement": {
             "$ref": "#/components/schemas/StatementSummary"
            },
            "payment": {
             "$ref": "#/components/schemas/InvoicePayment"
            }
           },
           "required": [
            "statement",
            "payment"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/redeemCoupon": {
   "post": {
    "operationId": "billing.redeemCoupon",
    "tags": [
     "billing"
    ],
    "summary": "Redeems a coupon code for the billing account.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/discounts` in the account.\n- **Audited as:** `billing:coupon-redeem`.\n- **Errors:** `CONFLICT` (409) when the account already redeemed the coupon; `NOT_FOUND` for a code that is unknown,\n  inactive, past `redeemBy` or out of redemptions (all answer the same); `INVALID_INPUT` when the tenant is not a\n  billing account.\n\nThe resulting discount applies to the account's invoices after the contract discount, in the order codes were\nredeemed. Voiding an invoice gives a one-off discount back.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#redeemcoupon"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "code": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "code"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DiscountView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/removeDiscount": {
   "post": {
    "operationId": "billing.removeDiscount",
    "tags": [
     "billing"
    ],
    "summary": "Ends a billing account's discount now.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/discounts`), called on the account.\n- **Audited as:** `billing:discount-remove`.\n- **Errors:** `INVALID_TRANSITION` (409) for a discount that has already ended.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#removediscount"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "discountId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "discountId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DiscountView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/renderInvoice": {
   "post": {
    "operationId": "billing.renderInvoice",
    "tags": [
     "billing"
    ],
    "summary": "Returns an invoice as a standalone HTML page to print or save as PDF.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing/statements`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` for an invoice outside the tenant's subtree.\n\nThe result is `{ filename, contentType, body }`. The page names the issuer (the `billing.issuer` option: name,\naddress, tax ID, contact, and `paymentInstructions` under the totals), the bill-to details, every line with its tier\nsub-lines, service period and proration, then discounts, coupons, credit, tax, payments, credit notes and the amount\ndue. It has no scripts and only inline styles, so it can be served with a strict content security policy.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#renderinvoice"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/InvoiceDocument"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/resumeSubscription": {
   "post": {
    "operationId": "billing.resumeSubscription",
    "tags": [
     "billing"
    ],
    "summary": "Undoes a cancellation at the end of the month before it takes effect.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans), or a root administrator.\n- **Audited as:** `billing:subscription-resume`.\n- **Errors:** `INVALID_TRANSITION` (409) for a subscription that is not set to cancel, or has ended.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#resumesubscription"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "subscriptionId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "subscriptionId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SubscriptionView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/revokeCredit": {
   "post": {
    "operationId": "billing.revokeCredit",
    "tags": [
     "billing"
    ],
    "summary": "Withdraws what is left of a credit.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/credits`).\n- **Audited as:** `billing:credit-revoke`.\n- **Errors:** `INVALID_TRANSITION` (409) for a credit already revoked.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#revokecredit"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "creditId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "creditId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CreditView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/setPrice": {
   "post": {
    "operationId": "billing.setPrice",
    "tags": [
     "billing"
    ],
    "summary": "Sets or removes a rate-card price for a meter the tenant defines: its list price, or a negotiated price for a tenant below it.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}` in the defining tenant.\n- **Audited as:** `billing:price`.\n- **Errors:** `NOT_FOUND` when the tenant does not define the meter; `INVALID_INPUT` for a reported meter, a target\n  outside the subtree, malformed tiers, or `effectiveFrom` more than 12 months back; `BILLING_PERIOD_CLOSED` (409) when\n  a month from `effectiveFrom` on is already invoiced for the tenants it reaches.\n\n`price` takes currency units: `per-unit` (`unitAmount`), `graduated` or `volume` (`tiers` of `upTo` and `unitAmount`,\noptional `flatAmount`, the last `upTo: null`), or `package` (`packageSize`, `packageAmount`), each with an optional\n`includedQuantity`. `effectiveFrom` (default this month) starts the price; `price: null` removes that entry.\n\n```ts\nawait iam.api.billing.setPrice(root, {\n  tenantId: rootTenantId,\n  meter: 'api-calls',\n  targetTenantId: acmeId,\n  price: { model: 'per-unit', unitAmount: 0.0003 },\n  note: 'Enterprise agreement',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#setprice"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "meter": {
          "type": "string"
         },
         "targetTenantId": {
          "type": "string"
         },
         "effectiveFrom": {
          "type": "string"
         },
         "price": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "object",
            "properties": {},
            "additionalProperties": {}
           }
          ]
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "meter",
         "price"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "anyOf": [
            {
             "type": "null"
            },
            {
             "$ref": "#/components/schemas/PriceView"
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/setProfile": {
   "post": {
    "operationId": "billing.setProfile",
    "tags": [
     "billing"
    ],
    "summary": "Creates or updates a billing profile: company, billing emails, tax ID, address, purchase order, cost center, payment terms.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/profile`; a new profile below an organization is created from an\n  ancestor with `targetTenantId`.\n- **Audited as:** `billing:profile`.\n- **Errors:** `ACCESS_DENIED` (403) when a tenant below an organization creates its own; `INVALID_INPUT` on the root\n  tenant or for more than 10 emails.\n\nA profile below an organization makes that tenant a billing account of its own, a decision for its parent; afterwards\nthe tenant's own billing managers may keep it up to date. `null` clears a field.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#setprofile"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "targetTenantId": {
          "type": "string"
         },
         "companyName": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "billingEmails": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "taxId": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "address": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "purchaseOrder": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "costCenter": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "paymentTermsDays": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ProfileView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/setTerms": {
   "post": {
    "operationId": "billing.setTerms",
    "tags": [
     "billing"
    ],
    "summary": "Sets a billing account's contract terms: a discount off the subtotal, a minimum monthly commitment, and the tax invoices add.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/terms`), called on the account.\n- **Audited as:** `billing:terms`.\n- **Errors:** `INVALID_INPUT` when the tenant is not a billing account, for a percentage outside 0 to 100, or a\n  negative commitment.\n\nInvoices issued from then on take the subtotal of their lines, subtract `discountPercent`, add the shortfall below\n`minimumCommitment` (currency units per month, monthly invoices only) as a true-up, subtract coupons and credit, and\nadd `taxRatePercent` (labelled `taxLabel`, `Tax` by default) on the rest. `null` clears a term. Spend reports stay at\nrate-card prices.\n\n```ts\nawait iam.api.billing.setTerms(root, {\n  tenantId: acmeId,\n  discountPercent: 10,\n  minimumCommitment: 1000,\n  taxRatePercent: 20,\n  taxLabel: 'VAT',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#setterms"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "discountPercent": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "minimumCommitment": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "taxRatePercent": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "taxLabel": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TermsView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/spend": {
   "post": {
    "operationId": "billing.spend",
    "tags": [
     "billing"
    ],
    "summary": "Reports the spend of the tenant and every tenant below it for a month, grouped and filtered.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.\n- **Errors:** `NOT_FOUND` for a team, department or sub-tenant outside the scope; `INVALID_INPUT` for an unknown\n  `groupBy` or a malformed month.\n\n`groupBy` is `meter` (default), `identity`, `agent`, `team`, `department`, `tenant`, `day` or `tag:{name}`. Filters:\n`meter`, `identityId`, `teamId`, `departmentId`, `subTenantId`, `rollUp: false` (no sub-teams or sub-departments) and\n`billableOnly` (leave out chargeback meters). Each row has `costMicros`, `amount`, `share` (percent) and `quantities`\nper meter; the current month carries a linear `forecast`. For showback, `shareUnattributed: true` (grouped by\n`identity`, `agent`, `team` or `department`) spreads the unattributed row over the other groups by their share of\nspend, reported as `sharedMicros` per row and for the report.\n\n```ts\nconst report = await iam.api.billing.spend(admin, { tenantId, groupBy: 'team' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#spend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "period": {
            "type": "string"
           },
           "groupBy": {
            "anyOf": [
             {
              "type": "string",
              "const": "tenant"
             },
             {
              "type": "string",
              "const": "agent"
             },
             {
              "type": "string",
              "const": "identity"
             },
             {
              "type": "string",
              "const": "team"
             },
             {
              "type": "string",
              "const": "department"
             },
             {
              "type": "string",
              "const": "meter"
             },
             {
              "type": "string",
              "const": "day"
             },
             {
              "type": "string"
             }
            ]
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/SpendFilters"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SpendReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/subscribe": {
   "post": {
    "operationId": "billing.subscribe",
    "tags": [
     "billing"
    ],
    "summary": "Subscribes the billing account to a plan (id or key).",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/subscriptions`: self-serve plans for the account's billing\n  managers, any plan for root administrators.\n- **Audited as:** `billing:subscription`.\n- **Errors:** `ACCESS_DENIED` (403) for a plan that is not self-serve, or `trialDays` from anyone but a root\n  administrator; `CONFLICT` (409) when the account already subscribes to the plan; `INVALID_TRANSITION` (409) for an\n  archived plan; `INVALID_INPUT` when the tenant is not a billing account.\n\n`seats` defaults to 1. Outside a trial the rest of this month's advance fees and seats are invoiced at once (the\nresult's `invoice`, `billingReason: 'subscription'`); after that each monthly invoice bills the month ahead. A trial\nbills nothing until it ends; the part of the month after it is billed on that month's invoice.\n\n```ts\nconst { subscription, invoice } = await iam.api.billing.subscribe(orgAdmin, {\n  tenantId: acmeId,\n  plan: 'team',\n  seats: 8,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#subscribe"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "plan": {
          "type": "string"
         },
         "seats": {
          "type": "number"
         },
         "trialDays": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "plan"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "subscription": {
             "$ref": "#/components/schemas/SubscriptionView"
            },
            "invoice": {
             "$ref": "#/components/schemas/StatementSummary"
            }
           },
           "required": [
            "subscription"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/teamSpend": {
   "post": {
    "operationId": "billing.teamSpend",
    "tags": [
     "billing"
    ],
    "summary": "Reports a team's spend, with the teams below it, grouped by identity by default.",
    "description": "- **Permission:** The team's maintainers (and those of teams above it), or `iam:billing:read`.\n- **Audited as:** `iam:billing:read`, with `metadata.via` `team-maintainer` or `permission`.\n- **Errors:** `ACCESS_DENIED` (403) for anyone else; `NOT_FOUND` for a team outside the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#teamspend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "period": {
          "type": "string"
         },
         "groupBy": {
          "anyOf": [
           {
            "type": "string",
            "const": "tenant"
           },
           {
            "type": "string",
            "const": "agent"
           },
           {
            "type": "string",
            "const": "identity"
           },
           {
            "type": "string",
            "const": "team"
           },
           {
            "type": "string",
            "const": "department"
           },
           {
            "type": "string",
            "const": "meter"
           },
           {
            "type": "string",
            "const": "day"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SpendReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/trend": {
   "post": {
    "operationId": "billing.trend",
    "tags": [
     "billing"
    ],
    "summary": "Returns monthly totals for the last months months (1 to 24, default 6), with the filters of spend.",
    "description": "- **Permission:** `iam:billing:read` on `iam/billing`.\n- **Audited as:** `iam:billing:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#trend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "months": {
            "type": "number"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/SpendFilters"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SpendTrend"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/updateBudget": {
   "post": {
    "operationId": "billing.updateBudget",
    "tags": [
     "billing"
    ],
    "summary": "Changes a budget's name, amount, period, meters, thresholds, alerts or enforcement; its subject stays.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/budgets`.\n- **Audited as:** `billing:budget-update`.\n- **Errors:** `CONFLICT` (409) for a name another budget uses; `NOT_FOUND` for a budget of another tenant.\n\n`meters: null` counts every meter again.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#updatebudget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "budgetId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "budgetId"
          ],
          "additionalProperties": false
         },
         {
          "type": "object",
          "properties": {
           "name": {
            "type": "string"
           },
           "period": {
            "type": "string",
            "enum": [
             "month",
             "quarter",
             "year"
            ]
           },
           "notify": {
            "type": "object",
            "properties": {
             "owners": {
              "type": "boolean"
             },
             "subject": {
              "type": "boolean"
             },
             "emails": {
              "type": "array",
              "items": {
               "type": "string"
              }
             }
            },
            "additionalProperties": false
           },
           "amount": {
            "type": "number",
            "description": "In currency units."
           },
           "thresholds": {
            "type": "array",
            "items": {
             "type": "number"
            }
           },
           "forecastAlerts": {
            "type": "boolean"
           },
           "enforce": {
            "type": "boolean"
           }
          },
          "additionalProperties": false
         },
         {
          "type": "object",
          "properties": {
           "meters": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "type": "array",
              "items": {
               "type": "string"
              }
             }
            ]
           }
          },
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/BillingBudgetView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/updateMeter": {
   "post": {
    "operationId": "billing.updateMeter",
    "tags": [
     "billing"
    ],
    "summary": "Renames a meter, changes its unit or description, or archives it.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.\n- **Audited as:** `billing:meter-update`.\n- **Errors:** `NOT_FOUND` when the tenant does not define the key; `INVALID_INPUT` when changing `aggregation` or\n  `pricing`.\n\nAn archived meter refuses new usage (`METER_ARCHIVED`) and keeps its history; `archived: false` restores it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#updatemeter"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "unit": {
          "type": "string"
         },
         "description": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "archived": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MeterView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/updatePlan": {
   "post": {
    "operationId": "billing.updatePlan",
    "tags": [
     "billing"
    ],
    "summary": "Changes a plan, by id or key: name, items, description, trial, self-serve, archived.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/plans`), called on the root tenant.\n- **Audited as:** `billing:plan-update`.\n- **Errors:** `NOT_FOUND` for an unknown plan; `INVALID_INPUT` as for `createPlan`.\n\n`null` clears `description` or `trialDays`. An archived plan takes no new subscriptions. Changes apply to invoices\ndrawn up afterwards.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#updateplan"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "plan": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "items": {
          "type": "array",
          "items": {}
         },
         "description": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "trialDays": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "selfServe": {
          "type": "boolean"
         },
         "archived": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "plan"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PlanView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/updateSubscription": {
   "post": {
    "operationId": "billing.updateSubscription",
    "tags": [
     "billing"
    ],
    "summary": "Changes a subscription's seats.",
    "description": "- **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans), or a root administrator.\n- **Audited as:** `billing:subscription-update`.\n- **Errors:** `INVALID_TRANSITION` (409) for an ended subscription; `INVALID_INPUT` for seats outside 0 to 1000000.\n\nSeats billed in advance for this month are prorated as invoice items on the next invoice (`invoiceItems`): added seats\nfor the rest of the month, credit for removed ones. Arrears seats follow the seat history on their own.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#updatesubscription"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "subscriptionId": {
          "type": "string"
         },
         "seats": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "subscriptionId",
         "seats"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "subscription": {
             "$ref": "#/components/schemas/SubscriptionView"
            },
            "invoiceItems": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/InvoiceItemView"
             }
            }
           },
           "required": [
            "subscription",
            "invoiceItems"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/billing/voidStatement": {
   "post": {
    "operationId": "billing.voidStatement",
    "tags": [
     "billing"
    ],
    "summary": "Voids a finalized statement: its credit, invoice items, coupons and advance-billed months come back and the month reopens.",
    "description": "- **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account\n  or the root tenant.\n- **Audited as:** `billing:statement-void`.\n- **Errors:** `INVALID_TRANSITION` (409) for a statement already void, a draft, or one with payments or credit notes\n  (issue a credit note instead).\n\nFix the usage, then close the month again: the new statement gets a new number. Credit the invoice created from a\nnegative balance is revoked.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/billing#voidstatement"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "statementId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "statementId",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/StatementSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/activate": {
   "post": {
    "operationId": "bindings.activate",
    "tags": [
     "bindings"
    ],
    "summary": "Activates an eligible binding for yourself so you hold its role for a limited time, or records an approval request when the binding requires one.",
    "description": "- **Permission:** `iam:bindings:activate` on the role (`iam/{roleId}`), from your own ordinary session of the\n  tenant. The binding must apply to you, directly or through a group you are a live member of.\n- **Audited as:** `iam:bindings:activate`, plus `binding:activate` (with `activationId`, `roleId`, `expiresAt`, and\n  the justification) or, when approval is required, `binding:activation-requested`.\n- **Errors:** `INVALID_TRANSITION` when the binding is not eligible, has not started, or has expired, or when it\n  requires approval from named approvers and none of them is active (an empty approver group and no active manager);\n  `ACCESS_DENIED` when the binding does not apply to you; `INVALID_INPUT` from an assumed-role session or a session\n  of another tenant, without a required justification, or for a `durationMs` outside one minute to the effective\n  maximum; `MFA_REQUIRED` when MFA is required and your session did not complete it; `IMPERSONATION_RESTRICTED` from\n  a \"view as\" session; `CONFLICT` while you already hold a live activation or a waiting request for this binding;\n  `INVARIANT_VIOLATION`.\n\n`durationMs` defaults to the effective maximum: the binding's `maxActivationMs`, capped by the tenant's access\npolicy. Without approval, the result has `status: 'active'` and `active: true`, and the role applies from the next\nrequest until `expiresAt`. With approval, the result has `status: 'pending'`; it lapses after the tenant's\n`approvalLifetimeMs` (24 hours by default) unless someone decides. When the deployment sends email, the approver\ngroup's live members and, with `managerApproval`, your manager receive an `activation-request`. An earlier activation that ended or was denied is replaced,\nso you can ask again after a refusal.\n\n```ts\nconst activation = await iam.api.bindings.activate(credential, {\n  tenantId,\n  bindingId,\n  durationMs: 30 * 60 * 1000,\n  justification: 'INC-4211: restart the payments worker',\n});\nif (activation.status === 'pending') {\n  // Waiting for an approver; bindings.listMine shows it as pendingActivation.\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#activate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "bindingId": {
          "type": "string"
         },
         "durationMs": {
          "type": "number"
         },
         "justification": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "bindingId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "status": {
             "type": "string",
             "enum": [
              "pending",
              "denied",
              "active"
             ]
            },
            "active": {
             "type": "boolean"
            },
            "bindingId": {
             "type": "string"
            },
            "identityId": {
             "type": "string"
            },
            "roleId": {
             "type": "string"
            },
            "activatedAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "justification": {
             "type": "string"
            },
            "sessionId": {
             "type": "string"
            },
            "requestedDurationMs": {
             "type": "number"
            },
            "decidedBy": {
             "type": "string"
            },
            "decidedAt": {
             "type": "number"
            },
            "note": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            }
           },
           "required": [
            "status",
            "active",
            "bindingId",
            "identityId",
            "roleId",
            "activatedAt",
            "expiresAt",
            "sessionId",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/approveActivation": {
   "post": {
    "operationId": "bindings.approveActivation",
    "tags": [
     "bindings"
    ],
    "summary": "Grants a pending activation request, so the requester holds the role from now until the approved duration ends.",
    "description": "- **Permission:** `iam:bindings:approve` on the role (`iam/{roleId}`). When the binding names approvers, you must\n  also be a live member of its approver group, the requester's manager (with `managerApproval`), or root.\n- **Audited as:** `iam:bindings:approve`, plus `binding:activation-approved` with the new `expiresAt` and your note.\n- **Errors:** `INVALID_TRANSITION` (409) when the request is no longer waiting (decided, withdrawn, or lapsed) or its\n  binding is no longer eligible and live; `INVALID_INPUT` when you decide your own request, for a `durationMs`\n  outside one minute to the effective maximum, or for a note over 2048 characters; `ACCESS_DENIED` when you are not\n  one of the designated approvers; `IMPERSONATION_RESTRICTED` from a \"view as\" session; `NOT_FOUND`;\n  `INVARIANT_VIOLATION`.\n\nWithout `durationMs`, the requester gets the duration they asked for, capped at the binding's effective maximum as\nit stands now (the tenant policy may have tightened since the request). Your `durationMs` replaces the requested\none, for example to grant a shorter window than asked. The activation starts when you approve, not when it was\nrequested. The requester is emailed `activation-decided` when the deployment sends email. Because nobody decides\ntheir own request and an impersonating administrator cannot decide in someone's name, approval gives you\ntwo-person control.\n\n```ts\nawait iam.api.bindings.approveActivation(credential, {\n  tenantId,\n  activationId,\n  durationMs: 45 * 60 * 1000,\n  note: 'Approved for the change window',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#approveactivation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "activationId": {
          "type": "string"
         },
         "note": {
          "type": "string"
         },
         "durationMs": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "activationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "status": {
             "type": "string",
             "enum": [
              "pending",
              "denied",
              "active"
             ]
            },
            "active": {
             "type": "boolean"
            },
            "bindingId": {
             "type": "string"
            },
            "identityId": {
             "type": "string"
            },
            "roleId": {
             "type": "string"
            },
            "activatedAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "justification": {
             "type": "string"
            },
            "sessionId": {
             "type": "string"
            },
            "requestedDurationMs": {
             "type": "number"
            },
            "decidedBy": {
             "type": "string"
            },
            "decidedAt": {
             "type": "number"
            },
            "note": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            }
           },
           "required": [
            "status",
            "active",
            "bindingId",
            "identityId",
            "roleId",
            "activatedAt",
            "expiresAt",
            "sessionId",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/create": {
   "post": {
    "operationId": "bindings.create",
    "tags": [
     "bindings"
    ],
    "summary": "Gives a role to a person, service account, or group under your grant authority, optionally temporary, future-dated, limited to a recurring window, or eligible for just-in-time activation.",
    "description": "- **Permission:** `iam:bindings:create` on the role (`iam/{roleId}`), plus an active grant authority.\n- **Audited as:** `iam:bindings:create`.\n- **Errors:** `PROTECTED_RESOURCE` for the protected Owner role; `NOT_FOUND` when the role, group, approver group, or\n  identity is not in this tenant, or the identity is deleted; `GRANT_AUTHORITY_REQUIRED` when you hold no active\n  grant authority; `ACCESS_DENIED` when `authorityId` names an authority that is not yours or is revoked;\n  `INVALID_INPUT` for an invalid subject type, date, or window, a `startsAt` that is not before `expiresAt`, or\n  activation settings on a binding that is not eligible; `CONFLICT` when the subject already has a binding of this\n  role under the same authority; `SOD_CONFLICT` when the binding would give someone a combination of roles a\n  [separation-of-duties rule](/docs/guides/authorization/separation-of-duties) forbids; `INVARIANT_VIOLATION` when it\n  would newly break an enforced [access invariant](/docs/reference/api/invariants).\n\nBind roles to groups where you can: people then gain and lose the role as they join and leave, without anyone\nediting bindings. The Owner role is never bound this way; ownership changes go through\n[`identities.setOwner`](/docs/reference/api/identities#setowner). An expired binding that the purge worker has not\nremoved yet still counts for `CONFLICT`; extend it with `update` instead of creating a new one.\n\n```ts\n// A contractor gets support access on weekdays, starting next week, for 90 days.\nconst startsAt = Date.now() + 7 * 86_400_000;\nawait iam.api.bindings.create(credential, {\n  tenantId,\n  roleId: support.id,\n  subjectType: 'identity',\n  subjectId: contractor.id,\n  startsAt,\n  expiresAt: startsAt + 90 * 86_400_000,\n  window: { from: '09:00', to: '17:00', timeZone: 'Europe/Berlin', days: [1, 2, 3, 4, 5] },\n});\n\n// The on-call group may take the incident-responder role for up to two hours, with a reason and MFA.\nawait iam.api.bindings.create(credential, {\n  tenantId,\n  roleId: responder.id,\n  subjectType: 'group',\n  subjectId: onCall.id,\n  eligible: true,\n  maxActivationMs: 2 * 60 * 60 * 1000,\n  requireJustification: true,\n  requireMfa: true,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BindingInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Binding"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/deactivate": {
   "post": {
    "operationId": "bindings.deactivate",
    "tags": [
     "bindings"
    ],
    "summary": "Ends your own activation early, or withdraws your own pending request.",
    "description": "- **Permission:** `iam:bindings:activate` on the role (`iam/{roleId}`); only the person the activation belongs to.\n- **Audited as:** `iam:bindings:activate`, plus `binding:deactivate` (with `cancelled: true` for a withdrawn\n  request).\n- **Errors:** `ACCESS_DENIED` when the activation belongs to someone else (administrators use `revokeActivation`);\n  `NOT_FOUND`; `INVARIANT_VIOLATION`.\n\nStep down when the work is done early: the role stops applying at the next request. The record is deleted, so you\ncan activate again later.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#deactivate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "activationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "activationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deactivated": {
             "type": "boolean"
            }
           },
           "required": [
            "deactivated"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/delete": {
   "post": {
    "operationId": "bindings.delete",
    "tags": [
     "bindings"
    ],
    "summary": "Removes a binding and every activation of it, so its subject stops holding the role at the next request.",
    "description": "- **Permission:** `iam:bindings:delete` on the binding (`iam/{bindingId}`), and the binding's own grant authority\n  (or root).\n- **Audited as:** `iam:bindings:delete`.\n- **Errors:** `ACCESS_DENIED` when another administrator's authority issued the binding; `PROTECTED_RESOURCE` for an\n  Owner binding; `NOT_FOUND` when the binding is not in this tenant; `INVARIANT_VIOLATION` when removing it would\n  break an enforced invariant that expects someone to keep access.\n\nTo take a group's role away from one person, remove them from the group instead; deleting the group binding\nremoves the role from every member.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "bindingId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "bindingId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/denyActivation": {
   "post": {
    "operationId": "bindings.denyActivation",
    "tags": [
     "bindings"
    ],
    "summary": "Refuses a pending activation request, optionally with a note for the requester.",
    "description": "- **Permission:** `iam:bindings:approve` on the role (`iam/{roleId}`), and designated-approver status when the\n  binding names approvers.\n- **Audited as:** `iam:bindings:approve`, plus `binding:activation-denied` with your note.\n- **Errors:** `INVALID_TRANSITION` (409) when the request is no longer waiting or its binding is no longer eligible\n  and live; `INVALID_INPUT` when you decide your own request or the note is over 2048 characters; `ACCESS_DENIED`\n  when you are not a designated approver; `IMPERSONATION_RESTRICTED`; `NOT_FOUND`.\n\nThe request becomes `status: 'denied'` and grants nothing. It stays visible through\n`listActivations({ status: 'denied' })` until the purge worker removes it. The requester is emailed\n`activation-decided` with your note when the deployment sends email, and may ask again with `activate`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#denyactivation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "activationId": {
          "type": "string"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "activationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "status": {
             "type": "string",
             "enum": [
              "pending",
              "denied",
              "active"
             ]
            },
            "active": {
             "type": "boolean"
            },
            "bindingId": {
             "type": "string"
            },
            "identityId": {
             "type": "string"
            },
            "roleId": {
             "type": "string"
            },
            "activatedAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "justification": {
             "type": "string"
            },
            "sessionId": {
             "type": "string"
            },
            "requestedDurationMs": {
             "type": "number"
            },
            "decidedBy": {
             "type": "string"
            },
            "decidedAt": {
             "type": "number"
            },
            "note": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            }
           },
           "required": [
            "status",
            "active",
            "bindingId",
            "identityId",
            "roleId",
            "activatedAt",
            "expiresAt",
            "sessionId",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/list": {
   "post": {
    "operationId": "bindings.list",
    "tags": [
     "bindings"
    ],
    "summary": "Lists the tenant's bindings, filtered by role, subject, eligibility, or upcoming expiry.",
    "description": "- **Permission:** `iam:bindings:read` on the role (`iam/{roleId}`) when you filter by `roleId`, otherwise on the\n  subject (`iam/{subjectId}`) when you filter by `subjectId`, otherwise on the tenant.\n- **Audited as:** `iam:bindings:read`.\n- **Errors:** `INVALID_INPUT` for an unknown `subjectType`, a non-boolean `eligible`, or an invalid `expiresBefore`.\n\nExpired bindings are left out unless you pass `includeExpired: true`; future-dated ones are included.\n`eligible: true` keeps only eligible bindings and `eligible: false` only standing ones, which is how you audit\nstanding privileged access. `expiresBefore` keeps temporary bindings that end at or before that time, for \"what\nends this month?\" reports. Group bindings are returned as stored, not expanded to members: use\n[`identities.listBindings`](/docs/reference/api/identities#listbindings) for one person's effective roles and\n[`roles.listBindings`](/docs/reference/api/roles#listbindings) for holders with names.\n\n```ts\nconst endingSoon = await iam.api.bindings.list(credential, {\n  tenantId,\n  expiresBefore: Date.now() + 14 * 86_400_000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         },
         "subjectType": {
          "type": "string",
          "enum": [
           "identity",
           "group"
          ]
         },
         "subjectId": {
          "type": "string"
         },
         "includeExpired": {
          "type": "boolean"
         },
         "eligible": {
          "type": "boolean"
         },
         "expiresBefore": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Binding"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/listActivations": {
   "post": {
    "operationId": "bindings.listActivations",
    "tags": [
     "bindings"
    ],
    "summary": "Lists activations and activation requests, newest first, filtered by binding, person, role, or status.",
    "description": "- **Permission:** `iam:bindings:read` on the first of `bindingId`, `identityId`, or `roleId` you filter by\n  (`iam/{id}`), otherwise on the tenant.\n- **Audited as:** `iam:bindings:read`.\n- **Errors:** `INVALID_INPUT` for a `status` other than `pending`, `active`, or `denied`.\n\nWithout `status`, the result is who is elevated right now: live activations only. `status: 'pending'` lists open\nrequests, `'active'` activations, and `'denied'` refusals (all of them, until the purge worker removes them).\n`includeExpired: true` adds records that have ended or lapsed but are not yet purged. Each record carries `status`\nand `active` (whether it grants at this moment).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#listactivations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "bindingId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         },
         "includeExpired": {
          "type": "boolean"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "denied",
           "active"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "status": {
              "type": "string",
              "enum": [
               "pending",
               "denied",
               "active"
              ]
             },
             "active": {
              "type": "boolean"
             },
             "bindingId": {
              "type": "string"
             },
             "identityId": {
              "type": "string"
             },
             "roleId": {
              "type": "string"
             },
             "activatedAt": {
              "type": "number"
             },
             "expiresAt": {
              "type": "number"
             },
             "justification": {
              "type": "string"
             },
             "sessionId": {
              "type": "string"
             },
             "requestedDurationMs": {
              "type": "number"
             },
             "decidedBy": {
              "type": "string"
             },
             "decidedAt": {
              "type": "number"
             },
             "note": {
              "type": "string"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             }
            },
            "required": [
             "status",
             "active",
             "bindingId",
             "identityId",
             "roleId",
             "activatedAt",
             "expiresAt",
             "sessionId",
             "id",
             "tenantId"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/listApprovals": {
   "post": {
    "operationId": "bindings.listApprovals",
    "tags": [
     "bindings"
    ],
    "summary": "Lists the pending activation requests you may decide on, oldest first, with the role and the requester.",
    "description": "- **Permission:** `iam:bindings:approve` on the tenant. A request is included only when you also hold\n  `iam:bindings:approve` on its role and are a designated approver where the binding names any.\n- **Audited as:** `iam:bindings:approve`.\n\nIt never includes your own requests or requests that have lapsed. Use it to build an approver's inbox; each entry\ncarries `role` (id and name) and `requester` (id, name, and email).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#listapprovals"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "type": "object",
              "properties": {
               "status": {
                "type": "string",
                "enum": [
                 "pending",
                 "denied",
                 "active"
                ]
               },
               "active": {
                "type": "boolean"
               },
               "bindingId": {
                "type": "string"
               },
               "identityId": {
                "type": "string"
               },
               "roleId": {
                "type": "string"
               },
               "activatedAt": {
                "type": "number"
               },
               "expiresAt": {
                "type": "number"
               },
               "justification": {
                "type": "string"
               },
               "sessionId": {
                "type": "string"
               },
               "requestedDurationMs": {
                "type": "number"
               },
               "decidedBy": {
                "type": "string"
               },
               "decidedAt": {
                "type": "number"
               },
               "note": {
                "type": "string"
               },
               "id": {
                "type": "string"
               },
               "tenantId": {
                "type": "string"
               }
              },
              "required": [
               "status",
               "active",
               "bindingId",
               "identityId",
               "roleId",
               "activatedAt",
               "expiresAt",
               "sessionId",
               "id",
               "tenantId"
              ],
              "additionalProperties": false
             },
             {
              "type": "object",
              "properties": {
               "role": {
                "type": "object",
                "properties": {
                 "id": {
                  "type": "string"
                 },
                 "name": {
                  "type": "string"
                 }
                },
                "required": [
                 "id",
                 "name"
                ],
                "additionalProperties": false
               },
               "requester": {
                "type": "object",
                "properties": {
                 "id": {
                  "type": "string"
                 },
                 "name": {
                  "type": "string"
                 },
                 "email": {
                  "type": "string"
                 }
                },
                "required": [
                 "id",
                 "name"
                ],
                "additionalProperties": false
               }
              },
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/listMine": {
   "post": {
    "operationId": "bindings.listMine",
    "tags": [
     "bindings"
    ],
    "summary": "Lists your own bindings, direct and through groups, with any live activation or waiting request, so you can see what you may elevate to.",
    "description": "- **Permission:** `iam:bindings:activate` on the tenant, from an ordinary session of the tenant.\n- **Audited as:** `iam:bindings:activate`.\n- **Errors:** `INVALID_INPUT` from an assumed-role session or a session of another tenant.\n\nIt needs no `iam:bindings:read`, so members see their own access without seeing everyone else's. Each entry is a\nbinding with `via` (`'identity'`, or `{ groupId }` for a group binding) and its `role`, plus `activation` (`id`,\n`activatedAt`, `expiresAt`) while one is live, `pendingActivation` (`id`, `requestedAt`, `expiresAt`) while a request\nwaits, and `inWindow` for bindings with an access window. Standing and eligible bindings are both listed;\nfuture-dated ones appear with their `startsAt`, and expired ones are left out.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/EffectiveBinding"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/revokeActivation": {
   "post": {
    "operationId": "bindings.revokeActivation",
    "tags": [
     "bindings"
    ],
    "summary": "Ends someone else's activation or pending request immediately, for incident response.",
    "description": "- **Permission:** `iam:bindings:delete` on the binding (`iam/{bindingId}`), and the binding's own grant authority\n  (or root), like deleting the binding.\n- **Audited as:** `iam:bindings:delete`, plus `binding:deactivate` with `revoked: true` and the holder's\n  `identityId`.\n- **Errors:** `ACCESS_DENIED` when another administrator's authority issued the binding; `NOT_FOUND`;\n  `INVARIANT_VIOLATION`.\n\nRevoking ends the elevation, not the entitlement: the binding stays eligible and the person can activate it again.\nTo stop that, delete the binding or remove the person from its group. Revoking does not end the person's sessions;\nuse [`identities.revokeSessions`](/docs/reference/api/identities#revokesessions) for that.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#revokeactivation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "activationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "activationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deactivated": {
             "type": "boolean"
            }
           },
           "required": [
            "deactivated"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/bindings/update": {
   "post": {
    "operationId": "bindings.update",
    "tags": [
     "bindings"
    ],
    "summary": "Changes a binding's start, expiry, access window, or eligibility settings in place.",
    "description": "- **Permission:** `iam:bindings:create` on the role (`iam/{roleId}`), and the binding's own grant authority (or\n  root).\n- **Audited as:** `iam:bindings:create`.\n- **Errors:** `ACCESS_DENIED` (\"Cannot mutate a higher authority binding\") when another administrator's authority\n  issued the binding; `PROTECTED_RESOURCE` for an Owner binding; `INVALID_INPUT` when nothing is given to change, or\n  under the same date, window, and setting rules as `create`; `NOT_FOUND`; `SOD_CONFLICT` when extending an already\n  expired binding would create a forbidden combination; `INVARIANT_VIOLATION`.\n\nPass `null` to clear `startsAt`, `expiresAt`, `window`, or `approverGroupId`. The binding keeps its id and its\nauthority. `eligible: false` turns an eligible binding into a standing one, drops its activation settings, and ends\nevery activation and pending request of it; `eligible: true` does the reverse, so the subject stops holding the role\nuntil they activate it. Editing a binding that an [access package](/docs/guides/privileged-access/access-packages)\ncreated takes it over: revoking the package no longer removes it.\n\n```ts\n// Extend a contractor's access by 30 days and drop the business-hours limit.\nawait iam.api.bindings.update(credential, {\n  tenantId,\n  bindingId,\n  expiresAt: Date.now() + 30 * 86_400_000,\n  window: null,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/bindings#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "bindingId": {
            "type": "string"
           },
           "startsAt": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "type": "number"
             }
            ]
           },
           "expiresAt": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "type": "number"
             }
            ]
           },
           "window": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "$ref": "#/components/schemas/AccessWindow"
             }
            ]
           }
          },
          "required": [
           "tenantId",
           "bindingId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/EligibilityInput"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Binding"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/close": {
   "post": {
    "operationId": "certifications.close",
    "tags": [
     "certifications"
    ],
    "summary": "Closes an open campaign and applies it, removing the bindings reviewers revoked.",
    "description": "- **Permission:** `iam:certifications:manage` on the campaign, with\n  [recent authentication](/docs/guides/authentication/sessions#recent-authentication).\n- **Audited as:** `iam:certifications:manage`, plus one `iam:bindings:delete` per removed binding.\n- **Errors:** `RECENT_AUTH_REQUIRED` when your sign-in is not recent or the credential is temporary;\n  `IMPERSONATION_RESTRICTED` from an impersonation session; `CONFLICT` when the campaign is already closed;\n  `NOT_FOUND` when it is not in this tenant; `INVARIANT_VIOLATION` when the removals would break an enforced\n  [invariant](/docs/guides/governance/change-safety).\n\nThe result is the closed campaign with `outcomes`, the number of items per outcome. Close as the administrator who\ngranted the bindings, or as root, to avoid `revocation-failed` items. The closed campaign keeps every decision and\noutcome until you delete it.\n\n```ts\nconst closed = await iam.api.certifications.close(credential, { tenantId, campaignId });\n// closed.outcomes: { kept: 41, revoked: 6, 'already-removed': 1, 'revocation-failed': 0 }\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#close"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "campaignId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/CertificationCampaign"
            },
            {
             "type": "object",
             "properties": {
              "outcomes": {
               "type": "object",
               "properties": {
                "kept": {
                 "type": "number"
                },
                "revoked": {
                 "type": "number"
                },
                "already-removed": {
                 "type": "number"
                },
                "revocation-failed": {
                 "type": "number"
                }
               },
               "required": [
                "kept",
                "revoked",
                "already-removed",
                "revocation-failed"
               ],
               "additionalProperties": false
              }
             },
             "required": [
              "outcomes"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/create": {
   "post": {
    "operationId": "certifications.create",
    "tags": [
     "certifications"
    ],
    "summary": "Opens a campaign over the tenant's current role bindings and notifies the reviewers.",
    "description": "- **Permission:** `iam:certifications:manage` on `iam/certifications/*`.\n- **Audited as:** `iam:certifications:manage`.\n- **Errors:** `LIMIT_EXCEEDED` (409) when more than 5000 bindings would be reviewed; `INVALID_INPUT` for an empty\n  name, a listed role that is unknown or protected, a `dueAt` that is not in the future, `autoClose` without `dueAt`,\n  or an invalid `subjectType`, `reviewerMode`, or `undecided`; `NOT_FOUND` when a reviewer is not in this tenant or\n  is deleted.\n\nThe campaign covers the live bindings of every non-protected role, or only of `roleIds`, optionally only those held\nby people or by groups (`subjectType`). Bindings that have not started yet are left out; eligible bindings are\nincluded and flagged. `undecided` (default `keep`) says what closing does with items nobody decided: `revoke` makes\nsilence mean removal. When the deployment sends email, each reviewer receives one `certification-review` message\nwith their own item count. The result carries the campaign, its `progress`, and `items`, the number of bindings it\ncovers.\n\n```ts\nconst campaign = await iam.api.certifications.create(credential, {\n  tenantId,\n  name: 'Q4 admin review',\n  roleIds: [adminRole.id, billingAdminRole.id],\n  reviewerMode: 'manager',\n  reviewerIds: [securityLead.id], // items without an active manager go here\n  dueAt: Date.parse('2026-12-15T17:00:00Z'),\n  autoClose: true,\n  undecided: 'revoke',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "roleIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "subjectType": {
          "type": "string",
          "enum": [
           "identity",
           "group"
          ]
         },
         "reviewerIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "reviewerMode": {
          "type": "string",
          "enum": [
           "named",
           "manager"
          ]
         },
         "dueAt": {
          "type": "number"
         },
         "autoClose": {
          "type": "boolean"
         },
         "undecided": {
          "type": "string",
          "enum": [
           "keep",
           "revoke"
          ]
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "progress": {
             "$ref": "#/components/schemas/CertificationProgress"
            },
            "items": {
             "type": "number"
            },
            "name": {
             "type": "string"
            },
            "status": {
             "type": "string",
             "enum": [
              "open",
              "closed"
             ]
            },
            "createdBy": {
             "type": "string"
            },
            "createdAt": {
             "type": "number"
            },
            "dueAt": {
             "type": "number"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "subjectType": {
             "type": "string",
             "enum": [
              "identity",
              "group"
             ]
            },
            "reviewerIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "reviewerMode": {
             "type": "string",
             "enum": [
              "named",
              "manager"
             ]
            },
            "autoClose": {
             "type": "boolean"
            },
            "undecided": {
             "$ref": "#/components/schemas/CertificationDecision"
            },
            "closedAt": {
             "type": "number"
            },
            "closedBy": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "progress",
            "items",
            "name",
            "status",
            "createdBy",
            "createdAt",
            "reviewerIds",
            "undecided",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/decide": {
   "post": {
    "operationId": "certifications.decide",
    "tags": [
     "certifications"
    ],
    "summary": "Records keep or revoke decisions on up to 200 items of an open campaign.",
    "description": "- **Permission:** `iam:certifications:review` on the campaign; when the campaign names reviewers, you must be one of\n  them, except for items assigned to you as a manager.\n- **Audited as:** `iam:certifications:review`.\n- **Errors:** `SELF_REVIEW` for an item that certifies your own access; `ACCESS_DENIED` when you are not a reviewer\n  of the campaign or the item is assigned to someone else's manager and you lack `iam:certifications:manage`;\n  `CONFLICT` when the campaign is closed; `INVALID_INPUT` for an empty batch, more than 200 entries, or a decision\n  other than `keep` or `revoke`; `NOT_FOUND` for an item that is not in this campaign.\n\nThe batch is atomic: one refused entry rejects them all. Deciding an item again replaces the earlier decision and\nits note. A `note` holds at most 500 characters.\n\n```ts\nawait iam.api.certifications.decide(reviewerCredential, {\n  tenantId,\n  campaignId,\n  decisions: [\n    { itemId: 'item_1', decision: 'keep' },\n    { itemId: 'item_2', decision: 'revoke', note: 'Moved to finance in July' },\n  ],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#decide"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         },
         "decisions": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/CertificationDecisionInput"
          }
         }
        },
        "required": [
         "tenantId",
         "campaignId",
         "decisions"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "recorded": {
             "type": "number"
            }
           },
           "required": [
            "recorded"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/delete": {
   "post": {
    "operationId": "certifications.delete",
    "tags": [
     "certifications"
    ],
    "summary": "Deletes a closed campaign and all its items.",
    "description": "- **Permission:** `iam:certifications:manage` on the campaign.\n- **Audited as:** `iam:certifications:manage`.\n- **Errors:** `CONFLICT` when the campaign is still open (close it first); `NOT_FOUND` when it is not in this tenant.\n\nClosed campaigns are evidence; delete one only when your retention period for review records has passed.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "campaignId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/get": {
   "post": {
    "operationId": "certifications.get",
    "tags": [
     "certifications"
    ],
    "summary": "Returns one campaign with its items and progress.",
    "description": "- **Permission:** `iam:certifications:read` on the campaign.\n- **Audited as:** `iam:certifications:read`.\n- **Errors:** `NOT_FOUND` when the campaign is not in this tenant.\n\nItems are sorted by role and subject, each with its decision, reviewer, and, once closed, its outcome. `progress`\ncounts `total`, `decided`, `keep`, and `revoke`. `mine: true` leaves out the items that certify your own access\n(directly or through a group), which you could not decide anyway.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         },
         "mine": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "campaignId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "progress": {
             "$ref": "#/components/schemas/CertificationProgress"
            },
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/CertificationItem"
             }
            },
            "name": {
             "type": "string"
            },
            "status": {
             "type": "string",
             "enum": [
              "open",
              "closed"
             ]
            },
            "createdBy": {
             "type": "string"
            },
            "createdAt": {
             "type": "number"
            },
            "dueAt": {
             "type": "number"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "subjectType": {
             "type": "string",
             "enum": [
              "identity",
              "group"
             ]
            },
            "reviewerIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "reviewerMode": {
             "type": "string",
             "enum": [
              "named",
              "manager"
             ]
            },
            "autoClose": {
             "type": "boolean"
            },
            "undecided": {
             "$ref": "#/components/schemas/CertificationDecision"
            },
            "closedAt": {
             "type": "number"
            },
            "closedBy": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "progress",
            "items",
            "name",
            "status",
            "createdBy",
            "createdAt",
            "reviewerIds",
            "undecided",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/list": {
   "post": {
    "operationId": "certifications.list",
    "tags": [
     "certifications"
    ],
    "summary": "Lists the tenant's campaigns, newest first, each with its progress.",
    "description": "- **Permission:** `iam:certifications:read` on `iam/certifications/*`.\n- **Audited as:** `iam:certifications:read`.\n\nPass `status: 'open'` or `'closed'` to filter.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "open",
           "closed"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "progress": {
              "$ref": "#/components/schemas/CertificationProgress"
             },
             "name": {
              "type": "string"
             },
             "status": {
              "type": "string",
              "enum": [
               "open",
               "closed"
              ]
             },
             "createdBy": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "dueAt": {
              "type": "number"
             },
             "roleIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "subjectType": {
              "type": "string",
              "enum": [
               "identity",
               "group"
              ]
             },
             "reviewerIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "reviewerMode": {
              "type": "string",
              "enum": [
               "named",
               "manager"
              ]
             },
             "autoClose": {
              "type": "boolean"
             },
             "undecided": {
              "$ref": "#/components/schemas/CertificationDecision"
             },
             "closedAt": {
              "type": "number"
             },
             "closedBy": {
              "type": "string"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             },
             "uniqueKey": {
              "type": "string"
             }
            },
            "required": [
             "progress",
             "name",
             "status",
             "createdBy",
             "createdAt",
             "reviewerIds",
             "undecided",
             "id",
             "tenantId"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/listMine": {
   "post": {
    "operationId": "certifications.listMine",
    "tags": [
     "certifications"
    ],
    "summary": "Lists the open campaigns that have items assigned to you as a manager, with only those items.",
    "description": "- **Permission:** None beyond an ordinary session of the tenant (not an assumed role or another tenant's session).\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` from a role session or a session of another tenant.\n\nThis is the data a manager's review screen needs. It returns only manager-mode assignments; named reviewers use\n`get` with `mine: true` instead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "name": {
              "type": "string"
             },
             "dueAt": {
              "type": "number"
             },
             "reviewerMode": {
              "type": "string",
              "enum": [
               "named",
               "manager"
              ]
             },
             "items": {
              "type": "array",
              "items": {
               "$ref": "#/components/schemas/CertificationItem"
              }
             },
             "progress": {
              "$ref": "#/components/schemas/CertificationProgress"
             }
            },
            "required": [
             "id",
             "name",
             "reviewerMode",
             "items",
             "progress"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/remind": {
   "post": {
    "operationId": "certifications.remind",
    "tags": [
     "certifications"
    ],
    "summary": "Emails every reviewer who still has undecided items a reminder with their pending count.",
    "description": "- **Permission:** `iam:certifications:manage` on the campaign.\n- **Audited as:** `iam:certifications:manage`, plus `certification:remind` with the counts.\n- **Errors:** `DELIVERY_REQUIRED` when the deployment has no email delivery callback; `CONFLICT` when the campaign\n  is closed; `NOT_FOUND` when it is not in this tenant.\n\nManagers are reminded of their assigned items and named reviewers of the undecided items that fall back to them.\nOnly active reviewers with an email address are counted. When the campaign names no reviewers, nobody is reminded of\nunassigned items. Returns `reminded` (people emailed) and `pending` (undecided items). Send one a few days before\n`dueAt`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#remind"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "campaignId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "reminded": {
             "type": "number"
            },
            "pending": {
             "type": "number"
            }
           },
           "required": [
            "reminded",
            "pending"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/certifications/review": {
   "post": {
    "operationId": "certifications.review",
    "tags": [
     "certifications"
    ],
    "summary": "Records a manager's keep or revoke decisions on up to 200 items assigned to them, without a certification permission.",
    "description": "- **Permission:** None beyond an ordinary, non-impersonated session of the tenant; every item must be assigned to\n  you.\n- **Audited as:** `certification:review`, with the number of keep and revoke decisions.\n- **Errors:** `ACCESS_DENIED` for an item that is not assigned to you, or from a role session or another tenant's\n  session; `IMPERSONATION_RESTRICTED` from an impersonation session; `SELF_REVIEW` for your own access; `CONFLICT`\n  when the campaign is closed; `INVALID_INPUT` for an empty batch, more than 200 entries, or an invalid decision.\n\nThis lets line managers take part in reviews without holding an administrator role: being assigned the item is the\nauthorization. Like `decide`, the batch is atomic and a later decision replaces an earlier one.\n\n```ts\nawait iam.api.certifications.review(managerCredential, {\n  tenantId,\n  campaignId,\n  decisions: [{ itemId, decision: 'revoke', note: 'No longer on the payments team' }],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/certifications#review"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         },
         "decisions": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/CertificationDecisionInput"
          }
         }
        },
        "required": [
         "tenantId",
         "campaignId",
         "decisions"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "recorded": {
             "type": "number"
            }
           },
           "required": [
            "recorded"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/config/apply": {
   "post": {
    "operationId": "config.apply",
    "tags": [
     "config"
    ],
    "summary": "Applies a configuration document to the tenant in one transaction, and returns the changes it made.",
    "description": "- **Permission:** `iam:config:apply` on the tenant, plus the permission of the equivalent direct call for every\n  change (for example `iam:roles:create` on the tenant, `iam:policies:update` on the policy,\n  `iam:bindings:create` on the role, `iam:groups:update` on the group, `iam:tenants:update` for the access\n  policy). New roles, policies, and bindings are created under your grant authority.\n- **Audited as:** `iam:config:apply`, and `config:apply` with metadata `prune`, the change counts, and `changed`\n  (one line per change).\n- **Errors:** `INVALID_INPUT` for a malformed document or an unknown reference; `INVALID_POLICY` or\n  `INVALID_ACTION` for a policy document storage would reject; `ACCESS_DENIED` naming the first change you are not\n  allowed to make; `GRANT_AUTHORITY_REQUIRED` when something must be created and you hold no grant authority;\n  `LIMIT_EXCEEDED` when a create exceeds the tenant's plan limits; `SOD_CONFLICT` when the result gives someone\n  roles a [separation-of-duties rule](/docs/guides/authorization/separation-of-duties) forbids together;\n  `INVARIANT_VIOLATION` when it would newly break an enforced access invariant.\n\nEvery change is authorized exactly like the direct API call, so the document can never do more than you could do\nby hand, and one failure rolls everything back: the tenant is never left half-applied. Changes run in dependency\norder: policies, roles, and groups are created before the bindings and packages that name them, and are deleted\nonly after those are gone. Changing a group binding's eligibility or activation rules replaces the binding, which\nends its current activations.\n\nAlways [`plan`](#plan) first and review the result. The `config-apply` [CLI command](/docs/reference/cli#config-apply)\nruns this call from a pipeline.\n\n```ts\nconst document = JSON.parse(await readFile('tenant.json', 'utf8'));\nconst plan = await iam.api.config.plan(credential, { tenantId, config: document });\nif (plan.summary.delete === 0) {\n  const result = await iam.api.config.apply(credential, { tenantId, config: document });\n  console.log(result.summary); // { create: 2, update: 1, delete: 0, unchanged: 14 }\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/config#apply"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "config": {},
         "prune": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "config"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "applied": {
             "type": "boolean",
             "const": true
            },
            "tenantId": {
             "type": "string"
            },
            "prune": {
             "type": "boolean"
            },
            "changes": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/ConfigChange"
             }
            },
            "summary": {
             "type": "object",
             "properties": {
              "create": {
               "type": "number"
              },
              "update": {
               "type": "number"
              },
              "delete": {
               "type": "number"
              },
              "unchanged": {
               "type": "number"
              }
             },
             "required": [
              "create",
              "update",
              "delete",
              "unchanged"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "applied",
            "tenantId",
            "prune",
            "changes",
            "summary"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/config/export": {
   "post": {
    "operationId": "config.export",
    "tags": [
     "config"
    ],
    "summary": "Returns the tenant's roles, policies, groups, bindings, and the other configuration kinds as a document that plan and apply accept.",
    "description": "- **Permission:** `iam:config:read` on the tenant.\n- **Audited as:** `iam:config:read`.\n\nUse it to bootstrap version control from a tenant configured by hand, or to copy one environment's model into\nanother. Group members are exported as lowercase email addresses (members without an email are left out), a role\nwhose inline document was written as a `permissions` list is exported as that list again, and `invariants`,\n`agreements`, `departments`, and `teams` appear only when the tenant has some. The `config-export` [CLI command](/docs/reference/cli#config-export) writes it to a\nfile.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/config#export"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TenantConfig"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/config/plan": {
   "post": {
    "operationId": "config.plan",
    "tags": [
     "config"
    ],
    "summary": "Shows, without writing anything, every change apply would make for a configuration document.",
    "description": "- **Permission:** `iam:config:read` on the tenant.\n- **Audited as:** `iam:config:read`.\n- **Errors:** `INVALID_INPUT` for a malformed document (wrong `version`, duplicate names, more than 1000 items in a\n  list) or an unknown reference.\n\nThe result lists each change with its kind, name, and action, and a `summary` with counts per action. Planning\ndoes not check whether you may make each change; `apply` does. Run it in CI on every pull request, and use the\n`config-plan` [CLI command](/docs/reference/cli#config-plan) with `--fail-on-drift` in a nightly job to catch\nchanges someone made by hand.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/config#plan"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "config": {},
         "prune": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "config"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ConfigPlan"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/create": {
   "post": {
    "operationId": "credentials.create",
    "tags": [
     "credentials"
    ],
    "summary": "Issues an API key for an active service account and returns its token, which is shown only once.",
    "description": "- **Permission:** `iam:credentials:create` on the service account (`iam/{identityId}`), with recent authentication and\n  an active grant authority.\n- **Audited as:** `iam:credentials:create`, on the service account.\n- **Errors:** `INVALID_IDENTITY` when the identity is not an active, unexpired service account; `NOT_FOUND` when it is\n  not in this tenant; `INVALID_INPUT` for both `scopes` and `policy`, an empty `scopes` list, or `expiresInSeconds`\n  outside 60 seconds to 365 days; `INVALID_ACTION` or `INVALID_POLICY` when the scopes or policy do not validate\n  against the catalog; `GRANT_AUTHORITY_REQUIRED` when the caller holds no grant authority; `RECENT_AUTH_REQUIRED`;\n  `IMPERSONATION_RESTRICTED`.\n\nBecause the permission is checked on the service account, you can let a team issue keys for its own integration\naccounts only. Give every key the narrowest scopes that work and a name that says where it is deployed.\n\nNew keys are 58 characters that start with `biam_key_` and end in a checksum, so secret scanners (and the\n`credentialTokenScanPattern` export of `@better-iam/auth`) can recognize a leaked one; keys issued before the format\nexisted keep working until they expire. For short-lived, narrower credentials derived from a key, such as one per CI\njob, use [`sts.getSessionToken`](/docs/reference/api/sts#getsessiontoken) with the key as the caller.\n\n```ts\nconst { token, credentialId, expiresAt } = await iam.api.credentials.create(credential, {\n  tenantId,\n  identityId: deployBotId,\n  name: 'github-actions',\n  description: 'Release workflow in acme/api',\n  scopes: ['deployments:create', 'deployments:read'],\n  expiresInSeconds: 30 * 86400,\n});\n// Store `token` in the CI secret store now; it cannot be read again.\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "expiresInSeconds": {
          "type": "number"
         },
         "policy": {
          "$ref": "#/components/schemas/PolicyDocument"
         },
         "scopes": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "token": {
             "type": "string"
            },
            "credentialId": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            },
            "name": {
             "type": "string"
            }
           },
           "required": [
            "token",
            "credentialId",
            "expiresAt",
            "name"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/get": {
   "post": {
    "operationId": "credentials.get",
    "tags": [
     "credentials"
    ],
    "summary": "Returns one API key's label, lifetime, scopes, and last use, without token material.",
    "description": "- **Permission:** `iam:credentials:read` on the key.\n- **Audited as:** `iam:credentials:read`.\n- **Errors:** `NOT_FOUND` when the key is not in this tenant; `INVALID_CREDENTIAL` when the id belongs to a session\n  that is not an API key.\n\n`scopes` is present when the key was issued with `scopes` (its policy is exactly that list); otherwise `policy` shows\nthe session policy, if any. `credentialAuthorityId` names the grant authority the key was issued under.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "credentialId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "credentialId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CredentialSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/list": {
   "post": {
    "operationId": "credentials.list",
    "tags": [
     "credentials"
    ],
    "summary": "Lists the API keys of the tenant or of one service account, newest first, optionally only the unused ones.",
    "description": "- **Permission:** `iam:credentials:read` on the service account when `identityId` is given, otherwise on the tenant.\n- **Audited as:** `iam:credentials:read`.\n- **Errors:** `INVALID_INPUT` when `unusedForMs` is negative or more than ten years.\n\n`unusedForMs` keeps only keys that have not authenticated a request in that long, counting keys never used since\nthey were issued. Expired keys are included, marked `expired: true`.\n\n```ts\nconst stale = await iam.api.credentials.list(credential, { tenantId, unusedForMs: 60 * 86_400_000 });\nfor (const key of stale)\n  await iam.api.credentials.revoke(credential, { tenantId, credentialId: key.id });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "unusedForMs": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/CredentialSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/revoke": {
   "post": {
    "operationId": "credentials.revoke",
    "tags": [
     "credentials"
    ],
    "summary": "Deletes an API key so it stops working immediately.",
    "description": "- **Permission:** `iam:credentials:revoke` on the key, with recent authentication.\n- **Audited as:** `iam:credentials:revoke`.\n- **Errors:** `NOT_FOUND` when the key is not in this tenant; `INVALID_CREDENTIAL` for any other kind of session\n  (user sessions and role sessions end through their own calls); `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nRevocation cannot be undone; issue a new key if the integration still needs access. To stop every key of an account\nat once, disable the account with [`serviceAccounts.setStatus`](/docs/reference/api/service-accounts#setstatus).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "credentialId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "credentialId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "boolean"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/rotate": {
   "post": {
    "operationId": "credentials.rotate",
    "tags": [
     "credentials"
    ],
    "summary": "Replaces an API key with a new token in one transaction, so the old token stops working the moment the new one exists.",
    "description": "- **Permission:** `iam:credentials:create` on the key, with recent authentication.\n- **Audited as:** `iam:credentials:create`.\n- **Errors:** `NOT_FOUND` when the key is not in this tenant; `INVALID_CREDENTIAL` when the id is not an API key;\n  `ACCESS_DENIED` unless the caller issued the key (holds its grant authority) or is the platform root, or when that\n  authority has been revoked; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nThe replacement gets a new `credentialId` and keeps the name, description, scopes or policy, and expiry of the old\nkey. Its usage history starts over, so it shows as unused until the integration uses it. Rotate on a schedule, or at\nonce when a token may have leaked; deploy the returned token before anything else, because the old one is already\ndead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#rotate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "credentialId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "credentialId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "token": {
             "type": "string"
            },
            "credentialId": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            },
            "name": {
             "type": "string"
            }
           },
           "required": [
            "token",
            "credentialId",
            "expiresAt",
            "name"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/credentials/update": {
   "post": {
    "operationId": "credentials.update",
    "tags": [
     "credentials"
    ],
    "summary": "Relabels an API key or moves its expiry; the token itself does not change.",
    "description": "- **Permission:** `iam:credentials:create` on the key; changing `expiresAt` also requires recent authentication and\n  the key's grant authority (the issuer, or the platform root).\n- **Audited as:** `iam:credentials:create`.\n- **Errors:** `INVALID_INPUT` when nothing is given to change, or `expiresAt` is not in the future or is more than a\n  year away; `NOT_FOUND`; `INVALID_CREDENTIAL`; `ACCESS_DENIED` when changing the expiry of a key issued under\n  another administrator's authority; `RECENT_AUTH_REQUIRED` when changing the expiry without recent authentication.\n\nPass `null` for `name` or `description` to clear it. `expiresAt` can shorten a key's life or extend it, including\nrenewing a key that has already expired, which then works again without a new token.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/credentials#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "credentialId": {
          "type": "string"
         },
         "name": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "description": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "credentialId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CredentialSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/activity": {
   "post": {
    "operationId": "delegations.activity",
    "tags": [
     "delegations"
    ],
    "summary": "Returns what happened under a delegation, newest first: its lifecycle and everything the agent did for the person.",
    "description": "- **Permission:** None for the person, the agent with its own key, or the agent's sponsor; anyone else needs\n  `iam:delegations:read` on the delegation.\n- **Audited as:** `iam:delegations:read` when read with the permission; not audited for the people involved.\n- **Errors:** `NOT_FOUND` when the delegation is not in this tenant (for a caller with the permission);\n  `ACCESS_DENIED` for anyone else; `INVALID_INPUT` for a `limit` outside 1 to 500 or a malformed `offset`, `from`, or\n  `to`.\n\nThe events are those whose resource is the delegation (`delegation:grant`, `delegation:request`,\n`delegation:approve`, `delegation:deny`, `delegation:assume`, `delegation:revoke`) and those recorded for the agent's\ndelegated sessions under it (`sessionContext.delegationId`), allowed and denied, together with the same for every\n[hand-off](#hand-offs) below it. It answers the person's question \"what did the agent do on my behalf?\". Page with `limit` (100 by default) and `offset`, and bound the time with `from`\nand `to` (epoch milliseconds).\n\n```ts\nconst trail = await iam.api.delegations.activity(aliceSession, { tenantId, delegationId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#activity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/ActivityQuery"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "delegationId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "delegationId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AuditEvent"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/approve": {
   "post": {
    "operationId": "delegations.approve",
    "tags": [
     "delegations"
    ],
    "summary": "Approves an agent's pending request, optionally with a different scope, lifetime, or session cap.",
    "description": "- **Permission:** The person the request names, in their own signed-in session with a recent sign-in.\n- **Audited as:** `delegation:approve`, with the agent and the new `expiresAt`.\n- **Errors:** `NOT_FOUND` when the request is not addressed to you (administrators included); `INVALID_TRANSITION`\n  (409) when it is no longer pending or has lapsed; `RECENT_AUTH_REQUIRED` without a recent sign-in;\n  `IMPERSONATION_RESTRICTED` from an impersonation session; `DELEGATION_NOT_ALLOWED` or `INVALID_IDENTITY` (409) when\n  the agent stopped accepting delegation or is no longer in good standing; `INVALID_INPUT`, `INVALID_ACTION`, or\n  `INVALID_POLICY` for a new scope or an out-of-range lifetime.\n\nPass `scopes` or `policy` (not both) to replace the scope the agent asked for, usually to narrow it; without either,\nthe requested scope stands. The delegation lasts `expiresInSeconds` from now (300 seconds to one year), by default as\nlong as the agent asked for (`requestedSeconds`). `maxSessionSeconds` (60 to 43200) caps each delegated session.\n`handoff` lets the agent hand work on ([Hand-offs](#hand-offs)). An agent's requested `handoff` counts only when you\nstate `handoff` here yourself; a plain approval (or `null`) leaves hand-offs out.\n\n```ts\nawait iam.api.delegations.approve(aliceSession, {\n  tenantId,\n  delegationId: request.id,\n  scopes: ['calendar:read'],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#approve"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/DelegationScopeInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "delegationId": {
            "type": "string"
           },
           "expiresInSeconds": {
            "type": "number"
           },
           "maxSessionSeconds": {
            "type": "number"
           }
          },
          "required": [
           "tenantId",
           "delegationId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/assume": {
   "post": {
    "operationId": "delegations.assume",
    "tags": [
     "delegations"
    ],
    "summary": "Opens a delegated session in which the agent acts for the person, and returns its bearer token once.",
    "description": "- **Permission:** The delegation's agent, with its own API key.\n- **Audited as:** `delegation:assume`, with the person, the new session's id, and `durationSeconds`.\n- **Errors:** `ACCESS_DENIED` for any other credential (a delegated token cannot open another session); `NOT_FOUND`\n  when the delegation is not this agent's; `DELEGATION_PENDING` (409) while the person has not decided;\n  `DELEGATION_INACTIVE` (403) when the delegation was denied, revoked, lapsed, or ended, or the person is no longer\n  active; `DELEGATION_NOT_ALLOWED` (403) when the agent's `delegable` is off; `LIMIT_EXCEEDED` (409) when the\n  delegation already holds 20 live sessions; `INVALID_INPUT` for a `durationSeconds` out of range or a malformed\n  `sessionName`; `INVALID_POLICY` or `INVALID_ACTION` for the scope-down `policy`.\n\nThe token's identity is the person (`session.identityId`); `session.agentId` and `session.delegationId` name the agent\nand the delegation. It lasts `durationSeconds`, from 60 up to the smaller of the agent's `maxDelegatedSessionSeconds`\n(3600 when unset) and the delegation's `maxSessionSeconds`, and by default 900 seconds or that limit if it is lower.\nIt never outlives the delegation or the agent key that opened it. `sessionName` (2 to 64 letters, digits, or `+=,.@_-`)\nappears in the session context of audit events. Each call also records `lastUsedAt` on the delegation.\n\n```ts\nconst { token, expiresAt, session } = await iam.api.delegations.assume(agentKey, {\n  tenantId,\n  delegationId,\n  durationSeconds: 600,\n  sessionName: 'triage-run-42',\n});\nawait iam.require({ token, tenantId, action: 'tickets:update', resource: { type: 'ticket', id: 'T-1' } });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#assume"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "delegationId": {
          "type": "string"
         },
         "durationSeconds": {
          "type": "number"
         },
         "sessionName": {
          "type": "string"
         },
         "policy": {
          "$ref": "#/components/schemas/PolicyDocument"
         }
        },
        "required": [
         "tenantId",
         "delegationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegatedCredential"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/decideConfirmation": {
   "post": {
    "operationId": "delegations.decideConfirmation",
    "tags": [
     "delegations"
    ],
    "summary": "Approves or rejects an agent's pending confirmation request.",
    "description": "- **Permission:** None beyond the person's own session (not impersonated) of the tenant; only the person the request\n  asks may decide it.\n- **Audited as:** `delegation:confirm` or `delegation:reject`, on the delegation, with the confirmation id, the\n  action, and the resource.\n- **Errors:** `NOT_FOUND` when the request does not exist or asks someone else; `INVALID_TRANSITION` (409) when it\n  was already decided or has lapsed; `DELEGATION_INACTIVE` (403) when the delegation ended meanwhile;\n  `ACCESS_DENIED` from any other credential; `INVALID_INPUT` when `approve` is not a boolean.\n\n`approve: true` opens the requested action on the requested resource, for the agent acting for the person under this\ndelegation, until `validSeconds` after the decision. `approve: false` rejects it; nothing opens. No recent sign-in is\nneeded, so the person can answer from a notification right away.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#decideconfirmation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "confirmationId": {
          "type": "string"
         },
         "approve": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "confirmationId",
         "approve"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ConfirmationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/deny": {
   "post": {
    "operationId": "delegations.deny",
    "tags": [
     "delegations"
    ],
    "summary": "Turns down an agent's pending request.",
    "description": "- **Permission:** The person the request names, in their own signed-in session.\n- **Audited as:** `delegation:deny`, with the agent.\n- **Errors:** `NOT_FOUND` when the request is not addressed to you; `INVALID_TRANSITION` (409) when it is no longer\n  pending or has lapsed; `IMPERSONATION_RESTRICTED` from an impersonation session.\n\nNo recent sign-in is needed. The request becomes `denied`; the agent sees that when it polls `get`, and may ask again\nwith a new `request`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#deny"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "delegationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "delegationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/get": {
   "post": {
    "operationId": "delegations.get",
    "tags": [
     "delegations"
    ],
    "summary": "Returns one delegation to the person, the agent, or the agent's sponsor, or to an administrator.",
    "description": "- **Permission:** None for the person, the agent (with its own key), or the agent's sponsor; anyone else needs\n  `iam:delegations:read` on the delegation.\n- **Audited as:** Not audited for the parties involved; `iam:delegations:read` for administrators.\n- **Errors:** `NOT_FOUND` when the delegation is not in this tenant.\n\nAn agent that asked with `request` polls this until `status` leaves `pending`. The summary names the agent (with its\nmodel and provider) and the person, the scope as `scopes` (when given as actions) and as the compiled `policy`, who\ncreated it (`requestedBy`), the agent's `reason`, and its times; `expired` is true for a lapsed request or an active\ndelegation past its end. It never contains session tokens.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "delegationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "delegationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/getConfirmation": {
   "post": {
    "operationId": "delegations.getConfirmation",
    "tags": [
     "delegations"
    ],
    "summary": "Returns one confirmation request to the person it asks or to the agent that made it.",
    "description": "- **Permission:** None: the person's own session, the agent's own key, or a delegated session of the same delegation.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `NOT_FOUND` when the request does not exist or is not visible to the caller; `ACCESS_DENIED` for any\n  other credential.\n\nAgents poll it while `status` is `pending`. `expired` is true for a pending request past its decision window and for\nan approval past its validity.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#getconfirmation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "confirmationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "confirmationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ConfirmationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/grant": {
   "post": {
    "operationId": "delegations.grant",
    "tags": [
     "delegations"
    ],
    "summary": "Lets an agent act for you, within a scope and for a limited time.",
    "description": "- **Permission:** Your own signed-in session of the tenant with a recent sign-in; no `iam:*` permission.\n- **Audited as:** `delegation:grant`, with the agent, `expiresAt`, and the scopes.\n- **Errors:** `DELEGATION_EXISTS` (409) when a pending request or an active delegation already links you and the agent;\n  `DELEGATION_NOT_ALLOWED` (403) when the agent does not accept delegation; `INVALID_IDENTITY` (409) when the agent is\n  not in good standing; `NOT_FOUND` when the id is not an agent of this tenant; `RECENT_AUTH_REQUIRED` without a\n  recent sign-in; `IMPERSONATION_RESTRICTED` from an impersonation session; `ACCESS_DENIED` from an API key, role\n  session, session token, or delegated session; `INVALID_INPUT` for a missing or doubled scope or an out-of-range\n  lifetime; `INVALID_ACTION` or `INVALID_POLICY` for a scope the catalog does not accept.\n\nGive exactly one of `scopes` (actions, wildcards allowed) or `policy` (a policy document). Either way the scope only\nnarrows: the agent acts with your grants, so it can never do more than you can. `expiresInSeconds` runs from 300\nseconds to one year (30 days by default), and `maxSessionSeconds` (60 to 43200) caps each delegated session below the\nagent's own limit. When the agent has already asked, approve its request instead. [`agents.catalog`](/docs/reference/api/agents#catalog)\nlists the agents you can delegate to.\n\n```ts\nconst delegation = await iam.api.delegations.grant(aliceSession, {\n  tenantId,\n  agentId,\n  scopes: ['tickets:read', 'tickets:update'],\n  expiresInSeconds: 30 * 86_400,\n  maxSessionSeconds: 900,\n});\n```\n\n`handoff` lets the agent hand parts of the delegation on to other agents ([Hand-offs](#hand-offs)); an invalid setting\n(an unknown agent, more than 20 agents, or a depth outside 1 to 3) is `INVALID_INPUT`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#grant"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/DelegationScopeInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "agentId": {
            "type": "string"
           },
           "expiresInSeconds": {
            "type": "number"
           },
           "maxSessionSeconds": {
            "type": "number"
           }
          },
          "required": [
           "tenantId",
           "agentId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/handoff": {
   "post": {
    "operationId": "delegations.handoff",
    "tags": [
     "delegations"
    ],
    "summary": "Hands part of the delegation an agent acts under on to another agent, for the same person.",
    "description": "- **Permission:** A delegated session (from `assume`) whose delegation allows hand-offs to that agent; no `iam:*`\n  permission.\n- **Audited as:** `delegation:handoff` on the new delegation, with `parentId`, `fromAgentId`, `toAgentId`, `depth`,\n  and the scopes; the actor is the person, with the handing agent in the session context.\n- **Errors:** `ACCESS_DENIED` for any credential but a delegated session of the tenant; `DELEGATION_INACTIVE` (403)\n  when the delegation (or one above it) has ended; `DELEGATION_NOT_ALLOWED` (403) when the person did not allow\n  hand-offs, not to that agent, or no more of them down the line, when the agent is already in the chain, or when it\n  does not accept delegation; `INVALID_IDENTITY` (409) when it is not in good standing; `NOT_FOUND` when the id is not\n  an agent of this tenant; `LIMIT_EXCEEDED` (409) when the delegation already holds 20 live hand-offs; `INVALID_INPUT`,\n  `INVALID_ACTION`, or `INVALID_POLICY` for the scope, `confirm`, or an `expiresInSeconds` outside 60 seconds to one\n  year.\n\nGive `agentId` and exactly one of `scopes` or `policy`. The result is a new active delegation from the same person to\nthat agent (see [Hand-offs](#hand-offs) for what bounds it). It lasts `expiresInSeconds` (one hour by default), never\npast the delegation above. It keeps the person's `confirm` list plus any `confirm` given here, and `maxSessionSeconds`\nnever above the delegation above. `reason` (up to 1024 characters) is shown to the person. Pass the new delegation's\n`id` to the other agent, for example in an A2A message; it opens its own sessions for the person with `assume` and its\nown key.\n\n```ts\nconst handoff = await iam.api.delegations.handoff(\n  { token: delegatedToken },\n  { tenantId, agentId: researcherId, scopes: ['documents:read'], reason: 'Find sources for the report' },\n);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#handoff"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "policy": {
            "$ref": "#/components/schemas/PolicyDocument"
           },
           "scopes": {
            "type": "array",
            "items": {
             "type": "string"
            },
            "description": "Actions the agent may take for the person (wildcards allowed); an alternative to `policy`."
           },
           "confirm": {
            "type": "array",
            "items": {
             "type": "string"
            },
            "description": "Action patterns the person confirms one call at a time (for example `billing:*`): refused with\n`CONFIRMATION_REQUIRED` until the person approves that action on that resource (`requestConfirmation`)."
           },
           "spend": {
            "anyOf": [
             {
              "type": "null"
             },
             {
              "type": "object",
              "properties": {
               "period": {
                "type": "string",
                "enum": [
                 "month",
                 "day",
                 "minute",
                 "hour"
                ]
               },
               "maxTokens": {
                "type": "number"
               },
               "maxCostUsd": {
                "type": "number"
               },
               "maxRequests": {
                "type": "number"
               }
              },
              "required": [
               "period"
              ],
              "additionalProperties": false
             }
            ],
            "description": "Caps what AI model calls made under the delegation (and hand-offs below it) may use per `period` (`minute`, `hour`,\n`day` or `month`): `maxTokens`, `maxCostUsd` and/or `maxRequests`. `null` in `approve` removes it."
           }
          },
          "additionalProperties": false
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "agentId": {
            "type": "string"
           },
           "expiresInSeconds": {
            "type": "number"
           },
           "maxSessionSeconds": {
            "type": "number"
           },
           "reason": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "agentId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/issueToken": {
   "post": {
    "operationId": "delegations.issueToken",
    "tags": [
     "delegations"
    ],
    "summary": "Gives an agent acting for a person a delegation token: a short-lived signed JWT that shows one service outside Better IAM that the agent acts for that person.",
    "description": "- **Permission:** A delegated session (from `assume`); no `iam:*` permission. The deployment needs the `a2a` option,\n  because its card keys sign the token.\n- **Audited as:** `delegation:token-issue` on the delegation, with the `audience`, `tokenId`, `expiresAt`, and the\n  scopes; the actor is the person, with the agent in the session context.\n- **Errors:** `FEATURE_DISABLED` (403) without the `a2a` option; `ACCESS_DENIED` for any credential but a delegated\n  session of the tenant; `DELEGATION_INACTIVE` (403) when the delegation or one above it has ended;\n  `DELEGATION_NOT_ALLOWED` (403) when:\n  - an agent in the chain does not list the audience in its `tokenAudiences`;\n  - some limit on the session does not allow a scope outright;\n  - a scope is one the person confirms call by call;\n  - a deny among the person's own grants could touch a scope.\n\n  `INVALID_INPUT` for an audience that is not an http(s) URL or other absolute URI, that has user info, a query or a\n  fragment, or that contains `*`; for a malformed scope (letters, digits, `:_./-`, and `*`; at most 50); or for a\n  `lifetimeSeconds` outside 30 to 3600. `RATE_LIMITED` (429) past the per-delegation budget.\n\nGive `audience`, the one service the token is for. It must match the\n[`tokenAudiences`](/docs/reference/api/agents#profile-and-ceiling) of the agent and of every agent that handed the work\nto it. `scopes` defaults to the delegation's own scopes (none for a delegation given as a policy). Each scope must be\nallowed outright, on every resource and without conditions, by every limit the session is under: the delegation and\nthose above it, the agents' ceilings, the session's scope-down policy, its key's scopes and issuer, and the person's\nand tenant's boundaries. No scope may be an action the person confirms call by call (`confirm` anywhere in the\nchain), nor one a deny statement among the person's own grants could touch. The token lasts `lifetimeSeconds` (300 by\ndefault), never past the session or any delegation in the chain, and is recorded until it expires so a live check can\nre-examine it.\n\nThe result is `{ token, tokenType: 'biam-delegation+jwt', tokenId, issuer, audience, scopes, chain, expiresAt,\nexpiresIn }`. The JWT's header is `{ alg, kid, typ: 'biam-delegation+jwt', jku }`. Its claims are `iss`, `sub` (the\nperson), `aud`, `iat`, `nbf`, `exp`, `jti`, `tenant_id`, `delegation_id`, `act`, and `scope` (space-separated, when\nthere are scopes). `act` is the agent (`{ sub }`), with the agents that handed the work on nested inside\n(RFC 8693 section 4.1). Services verify it with `verifyDelegationToken` from `@better-iam/a2a` against the deployment's\ncard keys. A service next to the deployment can use `iam.a2a.verifyDelegationToken(token, { audience, live: true })`.\nIt re-checks the delegation chain, the person, the agents and the acting agent's key, the audience, and the scopes\nagainst the current state, so a revocation or a narrowed limit takes effect before the token expires.\n\n```ts\nconst { token } = await iam.api.delegations.issueToken(\n  { token: delegatedToken },\n  { tenantId, audience: 'https://api.calendar.example', scopes: ['calendar:read'] },\n);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#issuetoken"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "audience": {
          "type": "string"
         },
         "scopes": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "lifetimeSeconds": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "audience"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationToken"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/list": {
   "post": {
    "operationId": "delegations.list",
    "tags": [
     "delegations"
    ],
    "summary": "Lists the tenant's delegations, newest first, filtered by agent, person, or status.",
    "description": "- **Permission:** `iam:delegations:read` on the tenant.\n- **Audited as:** `iam:delegations:read`.\n\n`status` is `pending`, `active`, `denied`, or `revoked`; lapsed requests and ended delegations keep their status and\ncarry `expired: true`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         },
         "subjectId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "denied",
           "active",
           "revoked"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DelegationSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/listConfirmations": {
   "post": {
    "operationId": "delegations.listConfirmations",
    "tags": [
     "delegations"
    ],
    "summary": "Lists confirmation requests newest first: those addressed to the caller, or those the calling agent made.",
    "description": "- **Permission:** None: a person's own session sees the requests that ask them, an agent's own key every request it\n  made, and a delegated session those of its delegation.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for any other credential or tenant.\n\nFilter with `status` (`pending`, `approved`, or `rejected`). It backs an \"actions waiting for your confirmation\" list.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#listconfirmations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "approved",
           "rejected"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConfirmationSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/listMine": {
   "post": {
    "operationId": "delegations.listMine",
    "tags": [
     "delegations"
    ],
    "summary": "Returns your own delegations, newest first: the agents acting or asking to act for you, or, for an agent, the people it acts for.",
    "description": "- **Permission:** A person's own signed-in session, or an agent's own API key, in the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for other credentials (role sessions, session tokens, delegated sessions, a session of\n  another tenant); `IMPERSONATION_RESTRICTED` from an impersonation session.\n\nEvery status is included, so a settings page can show requests to decide, delegations to revoke, and the history.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DelegationSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/request": {
   "post": {
    "operationId": "delegations.request",
    "tags": [
     "delegations"
    ],
    "summary": "Asks a person to delegate to the calling agent, and emails them the request when the deployment sends email.",
    "description": "- **Permission:** The agent's own API key.\n- **Audited as:** `delegation:request`, with the person and the scopes.\n- **Errors:** `ACCESS_DENIED` for any credential but an agent's own key; `RATE_LIMITED` (429) when the agent asks too\n  often; `NOT_FOUND` when no active person of the tenant has that id or email; `DELEGATION_EXISTS` (409) when a pending\n  request or an active delegation already links the agent and the person; `DELEGATION_NOT_ALLOWED` when the agent's\n  `delegable` is off; `INVALID_INPUT` without exactly one of `subjectId` and `subjectEmail`, without a `reason`, or for\n  scope and lifetime values as in `grant`.\n\nName the person by `subjectId` or `subjectEmail`; `reason` (up to 1024 characters) is shown to them. The request waits\nup to seven days for their decision. `expiresInSeconds` is how long the delegation will last once approved (reported as\n`requestedSeconds`); the person may change it and the scope when approving. When the person has an email address and\nthe deployment configures `sendEmail`, they receive the `delegation-request` email, which links to your approval page\nthrough `links.delegation` in the email templates. Each request counts toward a per-agent rate limit, even when it is\nrefused.\n\n```ts\nconst request = await iam.api.delegations.request(agentKey, {\n  tenantId,\n  subjectEmail: 'alice@acme.test',\n  scopes: ['calendar:read', 'calendar:write'],\n  reason: 'Schedule your interviews for next week',\n  expiresInSeconds: 7 * 86_400,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#request"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/DelegationScopeInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "subjectId": {
            "type": "string"
           },
           "subjectEmail": {
            "type": "string"
           },
           "reason": {
            "type": "string"
           },
           "expiresInSeconds": {
            "type": "number"
           },
           "maxSessionSeconds": {
            "type": "number"
           }
          },
          "required": [
           "tenantId",
           "reason"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/requestConfirmation": {
   "post": {
    "operationId": "delegations.requestConfirmation",
    "tags": [
     "delegations"
    ],
    "summary": "Asks the person an agent acts for to confirm one action the delegation holds back.",
    "description": "- **Permission:** None beyond the agent's delegated session (from `assume`) in the delegation's tenant.\n- **Audited as:** `delegation:confirmation-request`, on the delegation, with the confirmation id, the action, and the\n  resource.\n- **Errors:** `ACCESS_DENIED` for any credential other than a delegated session; `INVALID_INPUT` when the action does\n  not match the delegation's `confirm` patterns, or for a malformed action, resource, reason, or `validSeconds`\n  outside 30 to 3600; `RATE_LIMITED` (429) when the delegation asks too often.\n\nA delegation created with `confirm` (action patterns, on `grant`, `request`, or `approve`) refuses those actions to\nits delegated sessions until the person approves them one call at a time: the decision is refused with the internal\nreason `CONFIRMATION_REQUIRED` (callers see an ordinary denial). The agent names the `action` and the `resource` it\nwants to act on and a `reason` for the person; the request waits up to 30 minutes and is emailed to the person\n(template `delegation-confirmation`) when the deployment sends email. Asking again for the same action and resource\nwhile a request is pending returns that request. Once approved, exactly that action on that resource is allowed for\n`validSeconds` (300 by default); the agent polls `getConfirmation` and retries.\n\n```ts\nconst request = await iam.api.delegations.requestConfirmation(delegatedSession, {\n  tenantId,\n  action: 'documents:delete',\n  resource: { type: 'document', id: 'q3-draft' },\n  reason: 'You asked me to clean up the drafts folder',\n  validSeconds: 120,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#requestconfirmation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "action": {
          "type": "string"
         },
         "resource": {
          "type": "object",
          "properties": {
           "type": {
            "type": "string"
           },
           "id": {
            "type": "string"
           }
          },
          "required": [
           "type",
           "id"
          ],
          "additionalProperties": false
         },
         "reason": {
          "type": "string"
         },
         "validSeconds": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "action",
         "resource",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ConfirmationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/delegations/revoke": {
   "post": {
    "operationId": "delegations.revoke",
    "tags": [
     "delegations"
    ],
    "summary": "Ends a pending or active delegation and deletes its live delegated sessions at once.",
    "description": "- **Permission:** None for the person, the agent (with its own key), or the agent's sponsor; anyone else needs\n  `iam:delegations:revoke` on the delegation.\n- **Audited as:** `delegation:revoke`, with the agent, the person, `sessionsEnded`, `handoffsRevoked` (when hand-offs\n  below it ended too), and the `reason`; an administrator's call also as `iam:delegations:revoke`.\n- **Errors:** `INVALID_TRANSITION` (409) when it is already denied or revoked; `NOT_FOUND` when it is not in this\n  tenant.\n\n`reason` (up to 512 characters) goes to the audit event. A revoked delegation cannot be used again; the agent needs a\nnew `grant` or an approved `request`. To stop an agent for everyone at once, suspend it with\n[`agents.suspend`](/docs/reference/api/agents#suspend).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/delegations#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "delegationId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "delegationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DelegationSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/assign": {
   "post": {
    "operationId": "departments.assign",
    "tags": [
     "departments"
    ],
    "summary": "Places up to 100 people (identityIds, or one identityId) in a department, moving them out of any other, with an optional title. Returns how many changed.",
    "description": "- **Permission:** `iam:departments:manage` on `iam/{departmentId}`.\n- **Audited as:** `iam:departments:manage` and `department:assign` per person (`previousDepartmentId` when moved).\n- **Errors:** `INVALID_INPUT` for a service account or agent, or without people; `NOT_FOUND`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#assign"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         },
         "identityIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "identityId": {
          "type": "string"
         },
         "title": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "departmentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "assigned": {
             "type": "number"
            },
            "unchanged": {
             "type": "number"
            }
           },
           "required": [
            "assigned",
            "unchanged"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/create": {
   "post": {
    "operationId": "departments.create",
    "tags": [
     "departments"
    ],
    "summary": "Creates a department, optionally under parentId, with a code, a headId (an active person of the organization), a costCenter, and a description.",
    "description": "- **Permission:** `iam:departments:manage` on the tenant.\n- **Audited as:** `iam:departments:manage` and `department:create`.\n- **Errors:** `CONFLICT` (409) when the name or code (ignoring case) is taken; `INVALID_INPUT` for a bad code, a head\n  who is not a person, or more than twenty levels of nesting; `LIMIT_EXCEEDED` past 2000 departments.\n\n```ts\nconst engineering = await iam.api.departments.create(credential, {\n  tenantId,\n  name: 'Engineering',\n  code: 'ENG',\n  headId,\n  costCenter: 'CC-100',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DepartmentInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DepartmentDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/delete": {
   "post": {
    "operationId": "departments.delete",
    "tags": [
     "departments"
    ],
    "summary": "Deletes a department. Its people become unassigned and its teams lose the link.",
    "description": "- **Permission:** `iam:departments:manage` on the department.\n- **Audited as:** `iam:departments:manage` and `department:delete` (`unassigned`, `teams`).\n- **Errors:** `RESOURCE_IN_USE` (409) while departments sit below it, or while an access package rule names it\n  (`identity.departments`).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "departmentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "unassigned": {
             "type": "number"
            },
            "teams": {
             "type": "number"
            },
            "deleted": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "unassigned",
            "teams",
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/get": {
   "post": {
    "operationId": "departments.get",
    "tags": [
     "departments"
    ],
    "summary": "One department with its path (the departments above it), sub-departments, head, and teams, and member counts with and without the departments below.",
    "description": "- **Permission:** `iam:departments:read` on the department.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "departmentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DepartmentDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/importFromAttribute": {
   "post": {
    "operationId": "departments.importFromAttribute",
    "tags": [
     "departments"
    ],
    "summary": "Places every active person whose string identity attribute (such as department, filled by SCIM provisioning or an onboarding form) names a department, matched by name or code ignoring case.",
    "description": "- **Permission:** `iam:departments:manage` on the tenant.\n- **Audited as:** `iam:departments:manage`, and `department:create` / `department:assign` for what changed.\n- **Errors:** `INVALID_INPUT` when the attribute is not a declared string identity attribute.\n\n```ts\nconst result = await iam.api.departments.importFromAttribute(credential, {\n  tenantId,\n  attribute: 'department',\n  createMissing: true,\n  dryRun: true,\n});\n// { dryRun: true, created: ['Sales'], assigned: 12, unchanged: 30, unmatched: [], missing: 2 }\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#importfromattribute"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "attribute": {
          "type": "string"
         },
         "createMissing": {
          "type": "boolean"
         },
         "dryRun": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "attribute"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DepartmentImportResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/list": {
   "post": {
    "operationId": "departments.list",
    "tags": [
     "departments"
    ],
    "summary": "Every department with member counts (with and without sub-departments), child and team counts, in name order.",
    "description": "- **Permission:** `iam:departments:read` on the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DepartmentSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/listMembers": {
   "post": {
    "operationId": "departments.listMembers",
    "tags": [
     "departments"
    ],
    "summary": "The people of a department, heads first; includeSubdepartments adds those of every department below, each with their department.",
    "description": "- **Permission:** `iam:departments:read` on the department.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#listmembers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         },
         "includeSubdepartments": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "departmentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DepartmentMemberView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/mine": {
   "post": {
    "operationId": "departments.mine",
    "tags": [
     "departments"
    ],
    "summary": "Your own place in the org chart and, if you head departments, the people you lead.",
    "description": "- **Permission:** None beyond an ordinary session (or API key) of a person in the organization.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` for a service account, an agent, a temporary credential, or another tenant's session.\n\n`department` is your department with the path from the top, your title, since when, its head, and its cost center\n(null without a department). `leads` lists each department you head with its people and those of every department\nbelow it (name, email, department, title, and manager), heads first. Use it for a \"my team\" page that managers can\nopen without `iam:departments:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#mine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MyDepartment"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/ofIdentity": {
   "post": {
    "operationId": "departments.ofIdentity",
    "tags": [
     "departments"
    ],
    "summary": "A person's department with the path from the top, their title, since when, the department's head and cost center; null when they have none.",
    "description": "- **Permission:** `iam:departments:read` on `iam/{identityId}`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#ofidentity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "anyOf": [
            {
             "type": "null"
            },
            {
             "type": "object",
             "properties": {
              "costCenter": {
               "type": "string"
              },
              "head": {
               "$ref": "#/components/schemas/DepartmentPerson"
              },
              "since": {
               "type": "number"
              },
              "title": {
               "type": "string"
              },
              "department": {
               "$ref": "#/components/schemas/DepartmentRef"
              },
              "path": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/DepartmentRef"
               }
              }
             },
             "required": [
              "since",
              "department",
              "path"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/suggestBirthright": {
   "post": {
    "operationId": "departments.suggestBirthright",
    "tags": [
     "departments"
    ],
    "summary": "Roles and groups that most of a department's people already hold by hand, proposed as a ready-made automatic access package whose rule names the department.",
    "description": "- **Permission:** `iam:analysis:read` on the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `INVALID_INPUT` for `minShare` outside 0.5-1; `NOT_FOUND` for an unknown `departmentId`.\n\nA department's people are exactly who a rule naming it would match: active people placed in it or in a department\nbelow it. Only plain grants count (standing, permanent role bindings made to the person and permanent memberships of\nordinary groups, none from an access package), an item must be held by at least `minShare` (default 0.8) of at least\n`minPeople` (default 3) people, and nothing is suggested twice: not what is suggested for a department above, not\nwhat an automatic package naming the department (or one above) grants, and not what most of the department already\nreceives from any automatic package. Each suggestion carries the shares, `wouldGrant` (people who would gain\nsomething), `existingPackages`, and `package`, ready for [`packages.create`](/docs/reference/api/packages#create):\n\n```ts\nconst [suggestion] = await iam.api.departments.suggestBirthright(credential, {\n  tenantId,\n  departmentId: engineeringId,\n});\nif (suggestion) await iam.api.packages.create(credential, { tenantId, ...suggestion.package });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#suggestbirthright"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         },
         "minShare": {
          "type": "number"
         },
         "minPeople": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/BirthrightSuggestion"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/syncManagers": {
   "post": {
    "operationId": "departments.syncManagers",
    "tags": [
     "departments"
    ],
    "summary": "Makes department heads the managers of their departments' people (see above).",
    "description": "- **Permission:** `iam:identities:update` on the tenant (or the department) and `iam:departments:read`.\n- **Audited as:** `iam:identities:update` and `department:sync-managers`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#syncmanagers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "departmentId": {
          "type": "string"
         },
         "overwrite": {
          "type": "boolean"
         },
         "dryRun": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ManagerSyncResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/tree": {
   "post": {
    "operationId": "departments.tree",
    "tags": [
     "departments"
    ],
    "summary": "The org chart: top-level departments with their sub-departments, heads, and member counts.",
    "description": "- **Permission:** `iam:departments:read` on the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#tree"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/DepartmentNode"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/unassign": {
   "post": {
    "operationId": "departments.unassign",
    "tags": [
     "departments"
    ],
    "summary": "Takes a person out of their department.",
    "description": "- **Permission:** `iam:departments:manage` on `iam/{identityId}`.\n- **Audited as:** `iam:departments:manage` and `department:unassign`.\n- **Errors:** `NOT_FOUND` when the person has no department.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#unassign"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/departments/update": {
   "post": {
    "operationId": "departments.update",
    "tags": [
     "departments"
    ],
    "summary": "Renames, re-codes, moves (parentId, null for top level), or changes the head, cost center, or description of a department; null (or an empty string) clears an optional field.",
    "description": "- **Permission:** `iam:departments:manage` on the department.\n- **Audited as:** `iam:departments:manage` and `department:update` (`fields`).\n- **Errors:** `INVALID_INPUT` when moving under itself or a department below it, or past twenty levels; `CONFLICT` for\n  a taken name or code.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/departments#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DepartmentUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DepartmentDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/domains/add": {
   "post": {
    "operationId": "domains.add",
    "tags": [
     "domains"
    ],
    "summary": "Claims a domain for the tenant and returns the TXT record the organization must publish to prove control.",
    "description": "- **Permission:** `iam:domains:create` on `iam/domains/{domain}`.\n- **Audited as:** `iam:domains:create`.\n- **Errors:** `INVALID_INPUT` when the value is not a domain such as `example.com`; `DOMAIN_NOT_ALLOWED` for a\n  shared mailbox provider on the blocked list; `CONFLICT` when this tenant already claimed the domain;\n  `DOMAIN_TAKEN` when another tenant has verified it.\n\nThe result carries `dnsRecord` (`type`, `name`, `value`); show it to the administrator exactly as returned. The\nclaim grants nothing until it is verified.\n\n```ts\nconst claimed = await iam.api.domains.add(credential, { tenantId, domain: 'acme.com' });\n// Publish claimed.dnsRecord at the DNS provider, then call domains.verify.\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/domains#add"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "domain": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "domain"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "domain": {
             "type": "string"
            },
            "status": {
             "type": "string",
             "enum": [
              "pending",
              "verified"
             ]
            },
            "createdAt": {
             "type": "number"
            },
            "createdBy": {
             "type": "string"
            },
            "verifiedAt": {
             "type": "number"
            },
            "lastCheckedAt": {
             "type": "number"
            },
            "dnsRecord": {
             "type": "object",
             "properties": {
              "type": {
               "type": "string",
               "const": "TXT"
              },
              "name": {
               "type": "string"
              },
              "value": {
               "type": "string"
              }
             },
             "required": [
              "type",
              "name",
              "value"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "id",
            "tenantId",
            "domain",
            "status",
            "createdAt",
            "createdBy",
            "verifiedAt",
            "lastCheckedAt",
            "dnsRecord"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/domains/delete": {
   "post": {
    "operationId": "domains.delete",
    "tags": [
     "domains"
    ],
    "summary": "Releases a domain claim; a verified domain stops resolving to the tenant immediately.",
    "description": "- **Permission:** `iam:domains:delete` on `iam/domains/{domain}`.\n- **Audited as:** `iam:domains:delete`.\n- **Errors:** `NOT_FOUND` when the claim is not in this tenant.\n\nReleasing a verified domain frees it, so another organization can then claim and verify it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/domains#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "domainId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "domainId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/domains/discover": {
   "post": {
    "operationId": "domains.discover",
    "tags": [
     "domains"
    ],
    "summary": "Finds the organization that verified an email address's domain, with the sign-in rules it enforces.",
    "description": "- **Permission:** None: public. No credential is needed.\n- **Errors:** `INVALID_INPUT` when `email` has no `@` or the domain is malformed; `NOT_FOUND` when no active\n  organization verified the domain; `WRONG_REGION` (421) when another region serves the organization.\n\nThis is home-realm discovery for a login screen: the person types their email, you call `discover`, and you send\nthem to the right tenant with the right method. The answer carries the tenant's `tenantId`, `name`, `type`, alias\n(`slug`, when set), `allowedMethods` (`null` means every method the deployment enables), and `requireMfa`, plus its\nhome `region` and `signInUrl` when organization addresses or regions are configured. Pass either `email` or\n`domain`. In a multi-region deployment, an organization homed elsewhere answers `WRONG_REGION` with its sign-in URL\nthere, so a global sign-in page can send the person on.\n\nUnknown, pending, released, and inactive domains, and tenants under an inactive ancestor, all answer the same\n`NOT_FOUND`, so the call does not reveal which of these applies. The result is public discovery data by design;\napply ingress rate limits as you would for [`tenants.lookup`](/docs/reference/api/tenants#lookup).\n\n```ts\nconst org = await client.domains.discover({ email: 'alice@acme.com' });\nawait client.auth.signIn({ tenantId: org.tenantId, email: 'alice@acme.com', password });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/domains#discover"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "email": {
          "type": "string"
         },
         "domain": {
          "type": "string"
         }
        },
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/DomainDiscovery"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/domains/list": {
   "post": {
    "operationId": "domains.list",
    "tags": [
     "domains"
    ],
    "summary": "Lists the tenant's claimed domains, newest first, with their status and the TXT record each one needs.",
    "description": "- **Permission:** `iam:domains:read` on `iam/domains/*`.\n- **Audited as:** `iam:domains:read`.\n\nEach entry shows `status` (`pending` or `verified`), when it was verified, and when the DNS record was last\nchecked (`lastCheckedAt`), which helps an administrator see whether a failed verification was retried.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/domains#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             },
             "domain": {
              "type": "string"
             },
             "status": {
              "type": "string",
              "enum": [
               "pending",
               "verified"
              ]
             },
             "createdAt": {
              "type": "number"
             },
             "createdBy": {
              "type": "string"
             },
             "verifiedAt": {
              "type": "number"
             },
             "lastCheckedAt": {
              "type": "number"
             },
             "dnsRecord": {
              "type": "object",
              "properties": {
               "type": {
                "type": "string",
                "const": "TXT"
               },
               "name": {
                "type": "string"
               },
               "value": {
                "type": "string"
               }
              },
              "required": [
               "type",
               "name",
               "value"
              ],
              "additionalProperties": false
             }
            },
            "required": [
             "id",
             "tenantId",
             "domain",
             "status",
             "createdAt",
             "createdBy",
             "verifiedAt",
             "lastCheckedAt",
             "dnsRecord"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/domains/verify": {
   "post": {
    "operationId": "domains.verify",
    "tags": [
     "domains"
    ],
    "summary": "Looks up the domain's TXT record and marks the claim verified when the record matches.",
    "description": "- **Permission:** `iam:domains:update` on `iam/domains/{domain}`.\n- **Audited as:** `iam:domains:update`.\n- **Errors:** `NOT_FOUND` when the claim is not in this tenant; `DOMAIN_TAKEN` when another tenant verified the\n  domain first.\n\nA missing or not-yet-visible record is not an error: the call returns `verified: false`, records `lastCheckedAt`,\nand leaves the claim pending, so you can retry after DNS propagates. The DNS lookup runs before the database\ntransaction opens, and a failed lookup counts as \"not found\". Verifying a domain that is already verified returns\n`verified: true` without another lookup.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/domains#verify"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "domainId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "domainId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "verified": {
             "type": "boolean"
            },
            "domain": {
             "type": "object",
             "properties": {
              "id": {
               "type": "string"
              },
              "tenantId": {
               "type": "string"
              },
              "domain": {
               "type": "string"
              },
              "status": {
               "type": "string",
               "enum": [
                "pending",
                "verified"
               ]
              },
              "createdAt": {
               "type": "number"
              },
              "createdBy": {
               "type": "string"
              },
              "verifiedAt": {
               "type": "number"
              },
              "lastCheckedAt": {
               "type": "number"
              },
              "dnsRecord": {
               "type": "object",
               "properties": {
                "type": {
                 "type": "string",
                 "const": "TXT"
                },
                "name": {
                 "type": "string"
                },
                "value": {
                 "type": "string"
                }
               },
               "required": [
                "type",
                "name",
                "value"
               ],
               "additionalProperties": false
              }
             },
             "required": [
              "id",
              "tenantId",
              "domain",
              "status",
              "createdAt",
              "createdBy",
              "verifiedAt",
              "lastCheckedAt",
              "dnsRecord"
             ],
             "additionalProperties": false
            }
           },
           "required": [
            "verified",
            "domain"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/create": {
   "post": {
    "operationId": "features.create",
    "tags": [
     "features"
    ],
    "summary": "Defines a flag in the tenant: a platform flag on the root tenant, a flag for the tenant's subtree elsewhere.",
    "description": "- **Permission:** `iam:features:manage` on `iam/features/{key}`.\n- **Audited as:** `feature:create`, with the settings.\n- **Errors:** `CONFLICT` (409) when the tenant or one of its ancestors already defines the key; `LIMIT_EXCEEDED` (409)\n  past 200 flags in the tenant; `INVALID_INPUT` for a malformed key, an unknown field, `rolloutPercentage` with\n  `defaultValue: true`, or an `internal` flag that is also `tenantOverridable`; `INVALID_TRANSITION` in a deleted\n  tenant.\n\nKeys are lowercase letters and digits joined by `-`, `_`, or `.`, starting with a letter, at most 64 characters.\nSettings default to off, not overridable, no kill switch, not internal. `internal` flags are evaluated only by server\ncode and policies and are hidden from tenants below the defining one.\n\n```ts\nawait iam.api.features.create(rootCredential, {\n  tenantId: rootTenantId,\n  key: 'new-billing',\n  description: 'The redesigned billing pages',\n  tenantOverridable: true,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "defaultValue": {
          "type": "boolean"
         },
         "rolloutPercentage": {
          "type": "number"
         },
         "tenantOverridable": {
          "type": "boolean"
         },
         "killSwitch": {
          "type": "boolean"
         },
         "internal": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/FeatureFlagDefinition"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/delete": {
   "post": {
    "operationId": "features.delete",
    "tags": [
     "features"
    ],
    "summary": "Deletes a flag together with every target and override set for it.",
    "description": "- **Permission:** `iam:features:manage` on `iam/features/{key}`.\n- **Audited as:** `feature:delete`, with `removedTargets`.\n- **Errors:** `NOT_FOUND` when the tenant does not define the key.\n\nCode that still asks for the key gets `false` (reason `UNKNOWN`). The result is `{ success: true, removedTargets }`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            },
            "removedTargets": {
             "type": "number"
            }
           },
           "required": [
            "success",
            "removedTargets"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/evaluate": {
   "post": {
    "operationId": "features.evaluate",
    "tags": [
     "features"
    ],
    "summary": "Returns { tenantId, flags: { key: boolean } } for the flags that reach the tenant, for applications deciding what to show.",
    "description": "- **Permission:** None beyond a session (user, API key, role, or session token) of the tenant; root administrators may\n  evaluate any tenant.\n- **Audited as:** not audited.\n- **Errors:** `ACCESS_DENIED` (403) for a session of another tenant; `INVALID_INPUT` for a malformed key or more than\n  100 keys.\n\nInternal flags of ancestors are left out. `keys` limits the answer to those flags, and a requested key that no flag\ndefines comes back `false`. The React hooks `useFeatureFlags` and `useFeatureFlag` call it.\n\n```ts\nconst { flags } = await client.features.evaluate({ tenantId });\nif (flags['new-billing']) showNewBilling();\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#evaluate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "keys": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenantId": {
             "type": "string"
            },
            "flags": {
             "type": "object",
             "properties": {},
             "additionalProperties": {
              "type": "boolean"
             }
            }
           },
           "required": [
            "tenantId",
            "flags"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/list": {
   "post": {
    "operationId": "features.list",
    "tags": [
     "features"
    ],
    "summary": "The flags that reach the tenant, sorted by key, each with its value for the tenant and why.",
    "description": "- **Permission:** `iam:features:read` on `iam/features`.\n- **Audited as:** `iam:features:read`.\n\nEach entry has `evaluation` (`value`, `reason` of `KILL_SWITCH`, `TARGET`, `OVERRIDE`, `ROLLOUT`, or `DEFAULT`,\n`decidedBy`, `expiresAt`, `locked`, and `overridable`), the tenant's own `override`, and the `target` pinned for it.\nFlags the tenant defines also carry `definition` with their settings. Ancestors' internal flags are left out, except\nfor root administrators, who see every flag with its definition. `shadowed` lists flags this tenant defines whose key\nan ancestor also defines, so the ancestor's flag applies. Rename or delete them.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenantId": {
             "type": "string"
            },
            "flags": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/FeatureFlagView"
             }
            },
            "shadowed": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/FeatureFlagDefinition"
             }
            }
           },
           "required": [
            "tenantId",
            "flags",
            "shadowed"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/listTargets": {
   "post": {
    "operationId": "features.listTargets",
    "tags": [
     "features"
    ],
    "summary": "Every target and override below the defining tenant for one flag, newest first.",
    "description": "- **Permission:** `iam:features:read` on `iam/features/{key}`, in the defining tenant.\n- **Audited as:** `iam:features:read`.\n- **Errors:** `NOT_FOUND` when the tenant does not define the key.\n\nEach entry names the tenant (`tenantName`, `tenantStatus`), the `source` (`target` or `override`), the value,\n`locked`, `expiresAt`, the `note`, and `active`: false once a target has lapsed or an override is no longer allowed.\nOnly the defining tenant's managers see notes.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#listtargets"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/FeatureTargetView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/setOverride": {
   "post": {
    "operationId": "features.setOverride",
    "tags": [
     "features"
    ],
    "summary": "Records a tenant's own choice for a flag an ancestor defines with tenantOverridable: true.",
    "description": "- **Permission:** `iam:features:override` on `iam/features/{key}`, in the tenant.\n- **Audited as:** `feature:override`, with the value (`null` when withdrawn).\n- **Errors:** `FEATURE_LOCKED` (409) when the flag does not allow overrides or a locked target covers the tenant;\n  `NOT_FOUND` for a key no flag defines or an ancestor's internal flag; `INVALID_INPUT` when the tenant defines the flag\n  itself (change it with `update`).\n\nThe choice applies to the tenant and its descendants unless something closer decides. `value: null` withdraws it,\nwhich is always allowed. The result is the tenant's new evaluation.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#setoverride"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "value": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "key",
         "value"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/FeatureEvaluation"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/setTarget": {
   "post": {
    "operationId": "features.setTarget",
    "tags": [
     "features"
    ],
    "summary": "Pins a flag's value for a tenant below the defining tenant (tenantId), and for that tenant's descendants.",
    "description": "- **Permission:** `iam:features:manage` on `iam/features/{key}`, in the defining tenant.\n- **Audited as:** `feature:target`, recorded in the defining tenant with `targetTenantId`, `value`, `locked`, and\n  `expiresAt`.\n- **Errors:** `INVALID_INPUT` when `targetTenantId` is not below the defining tenant (use `defaultValue` for the tenant\n  itself) or `expiresAt` is not in the next ten years; `NOT_FOUND` when the tenant does not define the key.\n\n`locked: true` keeps the tenant and everything below it from overriding. `expiresAt` makes the target lapse by\nitself, for a trial or a temporary block. `note` is visible only to the defining tenant's managers. `value: null`\nremoves the target.\n\n```ts\nawait iam.api.features.setTarget(rootCredential, {\n  tenantId: rootTenantId,\n  key: 'fast-search',\n  targetTenantId: acmeId,\n  value: true,\n  expiresAt: Date.now() + 14 * 86_400_000,\n  note: 'Design partner trial',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#settarget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "targetTenantId": {
          "type": "string"
         },
         "value": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           }
          ]
         },
         "locked": {
          "type": "boolean"
         },
         "expiresAt": {
          "type": "number"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "key",
         "targetTenantId",
         "value"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "anyOf": [
            {
             "type": "null"
            },
            {
             "$ref": "#/components/schemas/FeatureTargetView"
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/features/update": {
   "post": {
    "operationId": "features.update",
    "tags": [
     "features"
    ],
    "summary": "Changes a flag's settings. Fields left out keep their value.",
    "description": "- **Permission:** `iam:features:manage` on `iam/features/{key}`.\n- **Audited as:** `feature:update`, with the settings `before` and `after`.\n- **Errors:** `NOT_FOUND` when the tenant does not define the key; `INVALID_INPUT` as for `create`.\n\n`null` clears `description` or `rolloutPercentage`. Turning `killSwitch` on switches the flag off for every tenant at\nonce, and turning it off restores the stored targets, overrides, and rollout. Turning `tenantOverridable` off keeps\nexisting overrides but ignores them.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/features#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "key": {
          "type": "string"
         },
         "description": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "defaultValue": {
          "type": "boolean"
         },
         "rolloutPercentage": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "tenantOverridable": {
          "type": "boolean"
         },
         "killSwitch": {
          "type": "boolean"
         },
         "internal": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "key"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/FeatureFlagDefinition"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/addMember": {
   "post": {
    "operationId": "groups.addMember",
    "tags": [
     "groups"
    ],
    "summary": "Adds a person to a group so they receive every role bound to the group, optionally until a given time.",
    "description": "- **Permission:** `iam:groups:update` on the group, plus grant authority for each of the group's role bindings.\n- **Audited as:** `iam:groups:update`.\n- **Errors:** `CONFLICT` when the person is already a live member; `NOT_FOUND` when the group or person is not in\n  this tenant; `ACCESS_DENIED` without authority over one of the group's bindings; `SOD_CONFLICT` when the\n  membership would give the person a combination of roles a\n  [separation-of-duties rule](/docs/guides/authorization/separation-of-duties) forbids.\n\nPass `expiresAt` to make the membership temporary. Adding someone whose earlier membership has lapsed renews it\ninstead of failing, and the renewed membership no longer belongs to the access package that originally created it.\n\n```ts\n// Give a contractor the team's access for 30 days.\nawait iam.api.groups.addMember(credential, {\n  tenantId,\n  groupId,\n  identityId,\n  expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#addmember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "groupId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/GroupMember"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/addMembers": {
   "post": {
    "operationId": "groups.addMembers",
    "tags": [
     "groups"
    ],
    "summary": "Adds up to 100 people to a group in one transaction, all with the same optional expiry.",
    "description": "- **Permission:** `iam:groups:update` on the group, plus grant authority for each of the group's role bindings.\n- **Audited as:** `iam:groups:update`.\n- **Errors:** `INVALID_INPUT` when `identityIds` is empty; any error `addMember` can raise for one person\n  (including `SOD_CONFLICT`) rejects the whole batch.\n\nUse it for cohort onboarding, such as a new class of employees or everyone joining a project on the same day.\nDuplicate ids are ignored. Because it is atomic, either everyone is added or no one is.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#addmembers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         },
         "identityIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "groupId",
         "identityIds"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "members": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/GroupMember"
             }
            }
           },
           "required": [
            "members"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/create": {
   "post": {
    "operationId": "groups.create",
    "tags": [
     "groups"
    ],
    "summary": "Creates a group in the tenant.",
    "description": "- **Permission:** `iam:groups:create` on the tenant.\n- **Audited as:** `iam:groups:create`.\n- **Errors:** `LIMIT_EXCEEDED` when the tenant's plan limit for groups is reached; `INVALID_INPUT` for an empty name\n  or a description over 512 characters.\n\nA new group is empty and grants nothing until you bind roles to it with\n[`bindings.create`](/docs/reference/api/bindings#create) and add members.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GroupInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Group"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/delete": {
   "post": {
    "operationId": "groups.delete",
    "tags": [
     "groups"
    ],
    "summary": "Deletes a group together with its memberships, its role bindings, their activations, and its relationship tuples.",
    "description": "- **Permission:** `iam:groups:delete` on the group, plus grant authority for each of its role bindings.\n- **Audited as:** `iam:groups:delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) when an access package still grants the group or names it in an automatic\n  assignment rule, or when the group approves package requests or eligible-binding activations.\n\nThe in-use checks exist so deleting a group never silently changes who can approve requests or what a package\ngrants: point those at another group first.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "groupId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/get": {
   "post": {
    "operationId": "groups.get",
    "tags": [
     "groups"
    ],
    "summary": "Returns one group by id.",
    "description": "- **Permission:** `iam:groups:read` on the group.\n- **Audited as:** `iam:groups:read`.\n- **Errors:** `NOT_FOUND` when the group is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "groupId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Group"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/list": {
   "post": {
    "operationId": "groups.list",
    "tags": [
     "groups"
    ],
    "summary": "Lists every group in the tenant.",
    "description": "- **Permission:** `iam:groups:read` on the tenant.\n- **Audited as:** `iam:groups:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Group"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/listMembers": {
   "post": {
    "operationId": "groups.listMembers",
    "tags": [
     "groups"
    ],
    "summary": "Lists the current members of a group, with membershipExpiresAt on temporary memberships.",
    "description": "- **Permission:** `iam:groups:read` on the group.\n- **Audited as:** `iam:groups:read`.\n- **Errors:** `NOT_FOUND` when the group is not in this tenant.\n\nLapsed memberships are left out even before the purge job removes them, so the list always matches who currently\nreceives the group's roles. Members are returned as public identities, without credential material.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#listmembers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "groupId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "$ref": "#/components/schemas/PublicIdentity"
             },
             {
              "type": "object",
              "properties": {
               "membershipExpiresAt": {
                "type": "number"
               }
              },
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/removeMember": {
   "post": {
    "operationId": "groups.removeMember",
    "tags": [
     "groups"
    ],
    "summary": "Removes a person from a group, ending the access the group's roles gave them.",
    "description": "- **Permission:** `iam:groups:update` on the group, plus grant authority for each of the group's role bindings.\n- **Audited as:** `iam:groups:update`.\n\nAny [just-in-time activations](/docs/guides/privileged-access/elevation) the person had of the group's eligible\nbindings end at the same time, so removing someone from a group cannot leave them elevated. Removing a person who is\nnot a member succeeds and changes nothing.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#removemember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "groupId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/update": {
   "post": {
    "operationId": "groups.update",
    "tags": [
     "groups"
    ],
    "summary": "Renames a group or changes its description.",
    "description": "- **Permission:** `iam:groups:update` on the group.\n- **Audited as:** `iam:groups:update`.\n- **Errors:** `INVALID_INPUT` when neither `name` nor `description` is given.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "groupId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Group"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/groups/updateMember": {
   "post": {
    "operationId": "groups.updateMember",
    "tags": [
     "groups"
    ],
    "summary": "Extends, shortens, or clears the expiry of an existing membership.",
    "description": "- **Permission:** `iam:groups:update` on the group, plus grant authority for each of the group's role bindings.\n- **Audited as:** `iam:groups:update`.\n- **Errors:** `NOT_FOUND` when the person is not a live member of the group.\n\nPass `expiresAt: null` to make a temporary membership permanent. Editing a membership that an access package\ncreated takes it over: revoking the package will no longer remove it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/groups#updatemember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "groupId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "expiresAt": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "groupId",
         "identityId",
         "expiresAt"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/GroupMember"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/hostnames/add": {
   "post": {
    "operationId": "hostnames.add",
    "tags": [
     "hostnames"
    ],
    "summary": "Claims a hostname for the organization and returns the DNS records that verify and route it.",
    "description": "- **Permission:** `iam:hostnames:create` on the tenant.\n- **Audited as:** `iam:hostnames:create`, resource `hostnames/{hostname}`.\n- **Errors:** `FEATURE_DISABLED` unless `hosts.customHostnames` is on; `INVALID_INPUT` for a malformed name;\n  `HOSTNAME_NOT_ALLOWED` for a name the deployment uses itself (its base URL, a trusted origin, or its organization\n  subdomain space); `CONFLICT` (409) when the organization already claimed it; `HOSTNAME_TAKEN` (409) when another\n  organization verified it; `LIMIT_EXCEEDED` past 20 hostnames per organization.\n\nThe name is lowercased and a trailing dot removed. The claim stays `pending`, and resolves to nothing, until\n[`verify`](#verify) sees the TXT record: `_better-iam-challenge.{hostname}` (the label follows\n`domains.recordName`) with the value `better-iam-hostname={token}`.\n\n```ts\nconst claimed = await iam.api.hostnames.add(credential, { tenantId, hostname: 'login.acme.com' });\n// claimed.dnsRecords.verification: { type: 'TXT', name: '_better-iam-challenge.login.acme.com', value: '…' }\n// claimed.dnsRecords.routing: { type: 'CNAME', name: 'login.acme.com', value: 'custom.signin.example.com' }\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/hostnames#add"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "hostname": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "hostname"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicHostname"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/hostnames/delete": {
   "post": {
    "operationId": "hostnames.delete",
    "tags": [
     "hostnames"
    ],
    "summary": "Releases a hostname; a verified one stops resolving to the organization at once.",
    "description": "- **Permission:** `iam:hostnames:delete` on the tenant.\n- **Audited as:** `iam:hostnames:delete`.\n- **Errors:** `NOT_FOUND` when the hostname is not the organization's.\n\nIf it was the primary address, sign-in URLs fall back to the organization's subdomain. Remove the DNS records\nafterwards, and revoke its TLS certificate if your certificate automation does not.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/hostnames#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "hostnameId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "hostnameId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/hostnames/list": {
   "post": {
    "operationId": "hostnames.list",
    "tags": [
     "hostnames"
    ],
    "summary": "Lists the organization's claimed hostnames, newest first, with their status and DNS records.",
    "description": "- **Permission:** `iam:hostnames:read` on the tenant.\n- **Audited as:** `iam:hostnames:read`.\n\nEach entry shows `status` (`pending` or `verified`), whether it is `primary`, its sign-in `url`, and the records to\npublish, so a settings page can show setup instructions until verification succeeds.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/hostnames#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicHostname"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/hostnames/setPrimary": {
   "post": {
    "operationId": "hostnames.setPrimary",
    "tags": [
     "hostnames"
    ],
    "summary": "Makes a verified hostname the organization's canonical sign-in address, or goes back to its subdomain.",
    "description": "- **Permission:** `iam:hostnames:update` on the tenant.\n- **Audited as:** `iam:hostnames:update`.\n- **Errors:** `INVALID_INPUT` when the hostname is not verified yet; `NOT_FOUND` when it is not the organization's.\n\nSign-in URLs from [`tenants.lookup`](/docs/reference/api/tenants#lookup), `iam.hosts.signInUrl`, and the `signInUrl`\nof every email the organization's people receive use the primary hostname. `hostnameId: null` clears it. The result\nhas the new primary hostname (or `null`) and the organization's `signInUrl`.\n\n```ts\nawait iam.api.hostnames.setPrimary(credential, { tenantId, hostnameId: claimed.id });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/hostnames#setprimary"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "hostnameId": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "hostnameId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "primary": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "$ref": "#/components/schemas/PublicHostname"
              }
             ]
            },
            "signInUrl": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "string"
              }
             ]
            }
           },
           "required": [
            "primary",
            "signInUrl"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/hostnames/verify": {
   "post": {
    "operationId": "hostnames.verify",
    "tags": [
     "hostnames"
    ],
    "summary": "Looks up the hostname's TXT record and, when it matches, marks the hostname verified so it starts resolving to the organization.",
    "description": "- **Permission:** `iam:hostnames:update` on the tenant.\n- **Audited as:** `iam:hostnames:update`.\n- **Errors:** `FEATURE_DISABLED` unless custom hostnames are on; `HOSTNAME_TAKEN` (409) when another organization\n  verified it first; `HOSTNAME_NOT_ALLOWED` when the deployment's own addresses changed to include it; `NOT_FOUND`\n  when it is not the organization's.\n\nDNS is queried before the transaction opens, through `domains.resolveTxt` when you configure one (for example DNS\nover HTTPS). While the record is not visible yet, the call succeeds with `verified: false` and records\n`lastCheckedAt`, so a settings page can poll it. A verified hostname stays verified; to re-prove control, delete it\nand claim it again.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/hostnames#verify"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "hostnameId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "hostnameId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "verified": {
             "type": "boolean"
            },
            "hostname": {
             "$ref": "#/components/schemas/PublicHostname"
            }
           },
           "required": [
            "verified",
            "hostname"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/acceptInvitation": {
   "post": {
    "operationId": "identities.acceptInvitation",
    "tags": [
     "identities"
    ],
    "summary": "Redeems a member invitation: creates the person's account with the invited email, applies the invitation's roles and groups, and signs them in.",
    "description": "- **Permission:** None: public. The token from the invitation email is the proof.\n- **Audited as:** `identity:invitation:accept`, with the new member as the actor and the inviter, roles, and groups\n  in the metadata.\n- **Errors:** `INVITATION_INVALID` when the token is unknown, already used, revoked, or expired, or the inviter's\n  grant authority was revoked; `TENANT_UNAVAILABLE` when the tenant or one of its ancestors is not active;\n  `INVALID_INPUT` when neither the call nor the invitation gives a name; `IDENTITY_EXISTS` when an account with that\n  email was created in the meantime; `LIMIT_EXCEEDED` at the tenant's member limit; `WEAK_PASSWORD` or\n  `BREACHED_PASSWORD` when the password fails the password rules; `NOT_FOUND` when one of the invitation's roles or\n  groups was deleted since; `SOD_CONFLICT` when the invitation's roles together break a\n  [separation-of-duties rule](/docs/guides/authorization/separation-of-duties).\n\nThe email counts as verified, because following the link proved control of the address. `name` overrides the name\nthe inviter suggested. The result is the new public identity plus either `{ token, session }` or an MFA challenge\n(`mfaRequired: true`) when the tenant requires MFA; continue with the\n[MFA flow](/docs/guides/authentication/mfa#completing-a-challenge). Over HTTP, a response that issues a session\nalso sets the session cookie. Everything happens in one transaction, so a failure leaves the invitation usable.\n\n```ts\nconst result = await client.identities.acceptInvitation({\n  tenantId: params.tenant,\n  token: params.token,\n  password: form.password,\n});\nif ('mfaRequired' in result) {\n  // enroll or verify the second factor, then continue\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#acceptinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "token": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "password": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "token",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/EnrollmentResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/create": {
   "post": {
    "operationId": "identities.create",
    "tags": [
     "identities"
    ],
    "summary": "Creates a person in the tenant, optionally with a password, a manager, and a scheduled deactivation date.",
    "description": "- **Permission:** `iam:identities:create` on the tenant.\n- **Audited as:** `iam:identities:create`.\n- **Errors:** `IDENTITY_EXISTS` (409) when the email is already used in this tenant; `LIMIT_EXCEEDED` at the\n  tenant's member limit; `WEAK_PASSWORD` or `BREACHED_PASSWORD` for a password the rules refuse; `INVALID_INPUT`\n  for an `expiresAt` that is not in the future or is more than ten years ahead, or a manager who is not active;\n  `NOT_FOUND` when the manager is not in this tenant; `INVARIANT_VIOLATION` when the new person would break an\n  enforced [access invariant](/docs/guides/governance/change-safety).\n\nThe account starts active, with an unverified email and no roles. Without a password the person cannot sign in\nwith one: send them a reset link with [`requestPasswordReset`](#requestpasswordreset), or use [`invite`](#invite)\ninstead, which lets them choose it. `expiresAt` (epoch milliseconds) schedules deactivation. Declared attributes\nare set with [`update`](#update) or [`createMany`](#createmany). After the call commits,\n[automatic access-package rules](/docs/guides/privileged-access/access-packages#automatic-assignment) are\nreconciled for the new person, so a matching package applies right away.\n\n```ts\nconst contractor = await iam.api.identities.create(credential, {\n  tenantId,\n  email: 'sam@contractor.example',\n  name: 'Sam Rivera',\n  managerId: teamLeadId,\n  expiresAt: Date.parse('2026-12-31T23:59:59Z'),\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "password": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         },
         "managerId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "email",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/createMany": {
   "post": {
    "operationId": "identities.createMany",
    "tags": [
     "identities"
    ],
    "summary": "Creates up to 100 people in one transaction, each with optional attributes, roles, groups, password, and expiry.",
    "description": "- **Permission:** `iam:identities:create` on the tenant. With roles, also `iam:bindings:create` on each role and an\n  active grant authority; with groups, `iam:groups:update` on each group and authority over each of its role\n  bindings.\n- **Audited as:** `iam:identities:create`.\n- **Errors:** `INVALID_INPUT` for an empty list, more than 100 entries, or an undeclared or mistyped attribute;\n  `ACCESS_DENIED` without the right to grant one of the roles or fill one of the groups; `PROTECTED_RESOURCE` for\n  the Owner role; `GRANT_AUTHORITY_REQUIRED` when roles are given and you hold no active grant authority;\n  `IDENTITY_EXISTS`, `LIMIT_EXCEEDED`, `SOD_CONFLICT`, and `INVARIANT_VIOLATION` as for single creation. Any\n  failure rejects the whole batch.\n\nUse it for migrations and cohort onboarding. Roles are bound directly to each person under your grant authority and\ngroup memberships are permanent. An entry's `expiresAt` is that identity's deactivation date, not an expiry for its\ngrants. Attributes are checked against `permissions.identityAttributes`. The rights to grant each role and fill\neach group are checked once for the whole batch, so an import can never grant more than you could bind by hand.\n\n```ts\nconst { identities } = await iam.api.identities.createMany(credential, {\n  tenantId,\n  identities: newHires.map((hire) => ({\n    email: hire.email,\n    name: hire.name,\n    attributes: { department: hire.department }, // declared in permissions.identityAttributes\n    groupIds: [everyoneGroupId],\n  })),\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#createmany"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identities": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "email": {
             "type": "string"
            },
            "name": {
             "type": "string"
            },
            "password": {
             "type": "string"
            },
            "attributes": {
             "type": "object",
             "properties": {},
             "additionalProperties": {
              "description": "Any JSON value"
             }
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "groupIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "expiresAt": {
             "type": "number"
            }
           },
           "required": [
            "email",
            "name"
           ],
           "additionalProperties": false
          }
         }
        },
        "required": [
         "tenantId",
         "identities"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "identities": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/PublicIdentity"
             }
            }
           },
           "required": [
            "identities"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/delete": {
   "post": {
    "operationId": "identities.delete",
    "tags": [
     "identities"
    ],
    "summary": "Removes a person or service account for good, leaving a tombstone so audit records still name who acted.",
    "description": "- **Permission:** `iam:identities:delete` on the identity, with recent authentication.\n- **Audited as:** `iam:identities:delete` and `identity:delete` (with the kind and former email).\n- **Errors:** `CONFLICT` when the identity is already deleted; `INVALID_INPUT` when you try to delete yourself;\n  `ACCESS_DENIED` for a root administrator unless you are root; `LAST_OWNER` or `LAST_ROOT_ADMIN` for the last\n  active owner or root administrator; `RECENT_AUTH_REQUIRED` when your sign-in is not recent.\n\nIn one transaction it ends every session, API key, remembered device, and pending challenge; deletes role bindings,\ngroup memberships, activations, package assignments and requests, relationships, boundaries, passkeys, MFA\nenrollment, password history, and external-provider mappings; revokes the grant authorities the identity held and\nits account links; cancels its pending access requests; and clears it as the manager of anyone who reported to it.\nRevoking its grant authorities means grants it issued as a delegated administrator stop applying (see\n[grant authorities](/docs/guides/authorization/roles#grant-authorities)).\n\nThe tombstone keeps the ID, name, and kind with `status: 'deleted'`. It has no sign-in email, phone, or password;\nthe former address is kept as `deletedEmail`. `list` leaves tombstones out unless asked, and `get` still returns\nthem. Unlike `offboard`, deletion hands nothing to a successor, so for leavers call [`offboard`](#offboard) first\nand `delete` after your retention period.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/export": {
   "post": {
    "operationId": "identities.export",
    "tags": [
     "identities"
    ],
    "summary": "Returns everything the tenant stores about one identity as JSON, to answer a data-subject access request.",
    "description": "- **Permission:** `iam:identities:read` on the identity, with recent authentication. Audit events are included only\n  when you also hold `iam:audit:read` on the tenant.\n- **Audited as:** `iam:identities:read` and `identity:export` (with the kind and whether audit events were\n  included).\n- **Errors:** `RECENT_AUTH_REQUIRED` when your sign-in is not recent; `NOT_FOUND` when the identity is not in this\n  tenant.\n\nThe export contains the public identity; its stored sessions and API keys without token hashes; whether MFA is\nenabled; passkey identifiers and transports; external-provider subjects; effective role bindings; groups;\nrelationships; access requests; boundaries; grant authorities; account links; and SCIM links. With\n`iam:audit:read` it adds the audit events the identity performed, newest first and at most 5,000, and\n`auditIncluded` tells you which you got. Secrets, password hashes, and tokens are never included. Deleted\nidentities can be exported too, which helps when a request arrives after the account was removed.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#export"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "exportedAt": {
             "type": "number"
            },
            "tenantId": {
             "type": "string"
            },
            "identity": {
             "$ref": "#/components/schemas/PublicIdentity"
            },
            "sessions": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "identityId": {
                "type": "string"
               },
               "createdAt": {
                "type": "number"
               },
               "expiresAt": {
                "type": "number"
               },
               "lastSeenAt": {
                "type": "number"
               },
               "authenticatedAt": {
                "type": "number"
               },
               "mfa": {
                "type": "boolean"
               },
               "kind": {
                "type": "string",
                "enum": [
                 "user",
                 "role",
                 "api-key",
                 "session-token",
                 "delegated"
                ]
               },
               "agentId": {
                "type": "string"
               },
               "delegationId": {
                "type": "string"
               },
               "mfaAuthenticatedAt": {
                "type": "number"
               },
               "credentialAuthorityId": {
                "type": "string"
               },
               "sessionName": {
                "type": "string"
               },
               "sourceIdentity": {
                "type": "string"
               },
               "sessionTags": {
                "type": "object",
                "properties": {},
                "additionalProperties": {
                 "type": "string"
                }
               },
               "sourcePolicy": {
                "$ref": "#/components/schemas/PolicyDocument"
               },
               "format": {
                "type": "string",
                "enum": [
                 "jwt"
                ]
               },
               "audience": {
                "type": "array",
                "items": {
                 "type": "string"
                }
               },
               "webIdentity": {
                "type": "object",
                "properties": {
                 "providerId": {
                  "type": "string"
                 },
                 "issuer": {
                  "type": "string"
                 },
                 "subject": {
                  "type": "string"
                 }
                },
                "required": [
                 "providerId",
                 "issuer",
                 "subject"
                ],
                "additionalProperties": false
               },
               "method": {
                "type": "string",
                "enum": [
                 "password",
                 "passwordless-email",
                 "passwordless-sms",
                 "passkey",
                 "federated",
                 "impersonation"
                ]
               },
               "client": {
                "$ref": "#/components/schemas/SessionClientInfo"
               },
               "name": {
                "type": "string"
               },
               "description": {
                "type": "string"
               },
               "impersonatorId": {
                "type": "string"
               },
               "impersonatorSessionId": {
                "type": "string"
               },
               "trustedDeviceId": {
                "type": "string"
               },
               "originalIdentityId": {
                "type": "string"
               },
               "sourceTenantId": {
                "type": "string"
               },
               "roleId": {
                "type": "string"
               },
               "trustId": {
                "type": "string"
               },
               "sourceSessionId": {
                "type": "string"
               },
               "sourceAuthorityIds": {
                "type": "array",
                "items": {
                 "type": "string"
                }
               },
               "policy": {
                "$ref": "#/components/schemas/PolicyDocument"
               },
               "previousSignIn": {
                "$ref": "#/components/schemas/SignInRecord"
               },
               "id": {
                "type": "string"
               },
               "tenantId": {
                "type": "string"
               }
              },
              "required": [
               "identityId",
               "createdAt",
               "expiresAt",
               "lastSeenAt",
               "authenticatedAt",
               "mfa",
               "kind",
               "id",
               "tenantId"
              ],
              "additionalProperties": {}
             }
            },
            "mfa": {
             "type": "object",
             "properties": {
              "enabled": {
               "type": "boolean"
              }
             },
             "required": [
              "enabled"
             ],
             "additionalProperties": false
            },
            "passkeys": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "id": {
                "type": "string"
               },
               "credentialId": {},
               "transports": {}
              },
              "required": [
               "id",
               "credentialId",
               "transports"
              ],
              "additionalProperties": false
             }
            },
            "externalIdentities": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "providerId": {},
               "issuer": {},
               "subject": {}
              },
              "required": [
               "providerId",
               "issuer",
               "subject"
              ],
              "additionalProperties": false
             }
            },
            "bindings": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/EffectiveBinding"
             }
            },
            "groups": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Group"
             }
            },
            "relationships": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Relationship"
             }
            },
            "accessRequests": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/AccessRequest"
             }
            },
            "boundaries": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/PolicyDocument"
             }
            },
            "grantAuthorities": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "id": {
                "type": "string"
               },
               "revoked": {
                "type": "boolean"
               },
               "parentAuthorityId": {
                "type": "string"
               }
              },
              "required": [
               "id",
               "revoked",
               "parentAuthorityId"
              ],
              "additionalProperties": false
             }
            },
            "links": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "id": {
                "type": "string"
               },
               "linkedIdentityId": {
                "type": "string"
               },
               "revoked": {
                "type": "boolean"
               }
              },
              "required": [
               "id",
               "linkedIdentityId",
               "revoked"
              ],
              "additionalProperties": false
             }
            },
            "scim": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "connectionId": {},
               "externalId": {},
               "userName": {},
               "active": {}
              },
              "required": [
               "connectionId",
               "externalId",
               "userName",
               "active"
              ],
              "additionalProperties": false
             }
            },
            "auditIncluded": {
             "type": "boolean"
            },
            "audit": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/AuditEvent"
             }
            }
           },
           "required": [
            "exportedAt",
            "tenantId",
            "identity",
            "sessions",
            "mfa",
            "passkeys",
            "externalIdentities",
            "bindings",
            "groups",
            "relationships",
            "accessRequests",
            "boundaries",
            "grantAuthorities",
            "links",
            "scim",
            "auditIncluded",
            "audit"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/get": {
   "post": {
    "operationId": "identities.get",
    "tags": [
     "identities"
    ],
    "summary": "Returns one identity by ID, including the tombstone of a deleted identity.",
    "description": "- **Permission:** `iam:identities:read` on the identity.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant.\n\nPassword hashes are never returned.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/impersonate": {
   "post": {
    "operationId": "identities.impersonate",
    "tags": [
     "identities"
    ],
    "summary": "Opens a short-lived \"view as\" session as a member, for support and troubleshooting, and returns its token.",
    "description": "- **Permission:** `iam:identities:impersonate` on the member, with recent authentication, and the tenant's\n  [authentication policy](/docs/guides/authentication/tenant-policy) must set `allowImpersonation`.\n- **Audited as:** `iam:identities:impersonate` and `identity:impersonate` (with the reason, the new session ID, and\n  its expiry).\n- **Errors:** `FEATURE_DISABLED` when the tenant does not allow impersonation; `ACCESS_DENIED` for an owner or root\n  administrator; `INVALID_INPUT` for yourself, a service account, a missing reason, or a `durationMs` outside one\n  minute to eight hours; `IMPERSONATION_RESTRICTED` unless you act through an ordinary session of your own;\n  `MFA_REQUIRED` when the member requires MFA and your session did not complete it; `IP_NOT_ALLOWED` or\n  `IP_BLOCKED` when the tenant's network rules refuse your address; `RECENT_AUTH_REQUIRED`.\n\nThe session lasts `durationMs` (one hour by default) and never outlives your own session. Each operation it\nattempts is allowed only when both the member and you may perform it, and it cannot do anything that needs recent\nauthentication, assume roles, or grant OAuth consent. Every audit record it produces carries `impersonatorId`,\npolicies see `principal.impersonated`, and the member sees the session in their own session list. Over HTTP the\ntoken is returned in the body only, never as a cookie, so keep it in a separate context such as a dedicated tab.\nSee [impersonation](/docs/guides/authentication/impersonation).\n\n```ts\nconst { token, session } = await iam.api.identities.impersonate(credential, {\n  tenantId,\n  identityId: memberId,\n  reason: 'Ticket 4821: export button missing',\n  durationMs: 30 * 60_000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#impersonate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         },
         "durationMs": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "token": {
             "type": "string"
            },
            "session": {
             "type": "object",
             "properties": {
              "identityId": {
               "type": "string"
              },
              "createdAt": {
               "type": "number"
              },
              "expiresAt": {
               "type": "number"
              },
              "lastSeenAt": {
               "type": "number"
              },
              "authenticatedAt": {
               "type": "number"
              },
              "mfa": {
               "type": "boolean"
              },
              "kind": {
               "type": "string",
               "enum": [
                "user",
                "role",
                "api-key",
                "session-token",
                "delegated"
               ]
              },
              "agentId": {
               "type": "string"
              },
              "delegationId": {
               "type": "string"
              },
              "mfaAuthenticatedAt": {
               "type": "number"
              },
              "credentialAuthorityId": {
               "type": "string"
              },
              "sessionName": {
               "type": "string"
              },
              "sourceIdentity": {
               "type": "string"
              },
              "sessionTags": {
               "type": "object",
               "properties": {},
               "additionalProperties": {
                "type": "string"
               }
              },
              "sourcePolicy": {
               "$ref": "#/components/schemas/PolicyDocument"
              },
              "format": {
               "type": "string",
               "enum": [
                "jwt"
               ]
              },
              "audience": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "webIdentity": {
               "type": "object",
               "properties": {
                "providerId": {
                 "type": "string"
                },
                "issuer": {
                 "type": "string"
                },
                "subject": {
                 "type": "string"
                }
               },
               "required": [
                "providerId",
                "issuer",
                "subject"
               ],
               "additionalProperties": false
              },
              "method": {
               "type": "string",
               "enum": [
                "password",
                "passwordless-email",
                "passwordless-sms",
                "passkey",
                "federated",
                "impersonation"
               ]
              },
              "client": {
               "$ref": "#/components/schemas/SessionClientInfo"
              },
              "name": {
               "type": "string"
              },
              "description": {
               "type": "string"
              },
              "impersonatorId": {
               "type": "string"
              },
              "impersonatorSessionId": {
               "type": "string"
              },
              "trustedDeviceId": {
               "type": "string"
              },
              "originalIdentityId": {
               "type": "string"
              },
              "sourceTenantId": {
               "type": "string"
              },
              "roleId": {
               "type": "string"
              },
              "trustId": {
               "type": "string"
              },
              "sourceSessionId": {
               "type": "string"
              },
              "sourceAuthorityIds": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "policy": {
               "$ref": "#/components/schemas/PolicyDocument"
              },
              "previousSignIn": {
               "$ref": "#/components/schemas/SignInRecord"
              },
              "id": {
               "type": "string"
              },
              "tenantId": {
               "type": "string"
              }
             },
             "required": [
              "identityId",
              "createdAt",
              "expiresAt",
              "lastSeenAt",
              "authenticatedAt",
              "mfa",
              "kind",
              "id",
              "tenantId"
             ],
             "additionalProperties": {}
            },
            "identity": {
             "$ref": "#/components/schemas/PublicIdentity"
            }
           },
           "required": [
            "token",
            "session",
            "identity"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/invite": {
   "post": {
    "operationId": "identities.invite",
    "tags": [
     "identities"
    ],
    "summary": "Invites a person to the tenant by email, with roles and groups they receive when they accept.",
    "description": "- **Permission:** `iam:identities:create` on the tenant. With roles, also `iam:bindings:create` on each role and an\n  active grant authority; with groups, `iam:groups:update` on each group and authority over each of its role\n  bindings.\n- **Audited as:** `iam:identities:create`.\n- **Errors:** `DELIVERY_REQUIRED` without an email delivery callback; `IDENTITY_EXISTS` when the email already\n  belongs to an identity in this tenant; `PROTECTED_RESOURCE` for the Owner role; `ACCESS_DENIED` without the\n  right to grant one of the roles or fill one of the groups; `GRANT_AUTHORITY_REQUIRED` when roles or groups are\n  given and you hold no active grant authority; `NOT_FOUND` for an unknown role or group.\n\nThe result has the invitation ID and expiry but never the token, which travels only in the email. `name` is a\nsuggestion the person can change when accepting. Nothing is granted until acceptance; see\n[Invitations](#invitations). Inviting the same address again does not cancel an earlier invitation, so use\n[`resendInvitation`](#resendinvitation) for a lost email. If the person already has a disabled account, re-enable\nit with [`setStatus`](#setstatus) instead.\n\n```ts\nconst invitation = await iam.api.identities.invite(credential, {\n  tenantId,\n  email: 'alice@example.com',\n  name: 'Alice Chen',\n  roleIds: [editorRoleId],\n  groupIds: [designGroupId],\n});\n// invitation.expiresAt: when the link stops working\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#invite"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "email": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "roleIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "groupIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId",
         "email"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "invitationId": {
             "type": "string"
            },
            "email": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "groupIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            }
           },
           "required": [
            "invitationId",
            "email",
            "expiresAt",
            "roleIds",
            "groupIds"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/list": {
   "post": {
    "operationId": "identities.list",
    "tags": [
     "identities"
    ],
    "summary": "Lists the tenant's people and service accounts, with filters and paging.",
    "description": "- **Permission:** `iam:identities:read` on the tenant.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `INVALID_INPUT` for an unknown `kind` or `status`, or a `limit` outside 1 to 1,000.\n\nResults are ordered by name, then ID. `kind` (`user` or `service`) and `status` (`active`, `disabled`, `deleted`)\nnarrow the list; tombstones are left out unless you pass `includeDeleted` or ask for `status: 'deleted'`. `query`\nmatches the name or email case-insensitively. `expiresBefore` keeps identities whose scheduled deactivation is at\nor before that time, including ones already past it. `limit` and `offset` page through the result.\n\n```ts\n// Active accounts that end within the next 14 days.\nconst ending = await iam.api.identities.list(credential, {\n  tenantId,\n  status: 'active',\n  expiresBefore: Date.now() + 14 * 86_400_000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "kind": {
          "type": "string",
          "enum": [
           "user",
           "agent",
           "service"
          ]
         },
         "status": {
          "type": "string",
          "enum": [
           "active",
           "disabled",
           "deleted"
          ]
         },
         "includeDeleted": {
          "type": "boolean"
         },
         "query": {
          "type": "string"
         },
         "expiresBefore": {
          "type": "number"
         },
         "limit": {
          "type": "number"
         },
         "offset": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicIdentity"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/listBindings": {
   "post": {
    "operationId": "identities.listBindings",
    "tags": [
     "identities"
    ],
    "summary": "Lists an identity's role bindings, direct and through its groups, with their activation and window state.",
    "description": "- **Permission:** `iam:bindings:read` on the identity.\n- **Audited as:** `iam:bindings:read`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant.\n\nEach entry is the binding with its `role` and `via` (`'identity'`, or `{ groupId }` for a group binding).\n[Eligible bindings](/docs/guides/privileged-access/elevation) carry `activation` while activated and\n`pendingActivation` while a request awaits approval, and bindings with an access window carry `inWindow`.\nFuture-dated bindings are listed with their start; expired bindings and lapsed memberships are left out. The list\nshows what is bound, not a decision: to see why a specific action is allowed, use\n[access paths](/docs/guides/governance/access-paths).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#listbindings"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/EffectiveBinding"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/listGroups": {
   "post": {
    "operationId": "identities.listGroups",
    "tags": [
     "identities"
    ],
    "summary": "Lists the groups an identity currently belongs to, with membershipExpiresAt on temporary memberships.",
    "description": "- **Permission:** `iam:groups:read` on the identity.\n- **Audited as:** `iam:groups:read`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant.\n\nLapsed memberships are left out even before the purge job removes them, so the list matches the groups that\ncurrently give the identity roles.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#listgroups"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "$ref": "#/components/schemas/Group"
             },
             {
              "type": "object",
              "properties": {
               "membershipExpiresAt": {
                "type": "number"
               }
              },
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/listInvitations": {
   "post": {
    "operationId": "identities.listInvitations",
    "tags": [
     "identities"
    ],
    "summary": "Lists every member invitation of the tenant, pending or not, without tokens.",
    "description": "- **Permission:** `iam:identities:read` on the tenant.\n- **Audited as:** `iam:identities:read`.\n\nEach invitation shows the email, suggested name, roles, groups, the inviter, the grant authority its roles will be\nissued under (present only when it carries roles or groups), when it was created and expires, and whether it was\n`consumed` or `revoked`. An invitation past `expiresAt` that is neither has simply expired;\n[`resendInvitation`](#resendinvitation) renews it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#listinvitations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "email": {
              "type": "string"
             },
             "name": {
              "type": "string"
             },
             "roleIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "groupIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "authorityId": {
              "type": "string"
             },
             "inviterId": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "expiresAt": {
              "type": "number"
             },
             "consumed": {
              "type": "boolean"
             },
             "revoked": {
              "type": "boolean"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             }
            },
            "required": [
             "email",
             "roleIds",
             "groupIds",
             "inviterId",
             "createdAt",
             "expiresAt",
             "consumed",
             "id",
             "tenantId"
            ],
            "additionalProperties": {}
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/listReports": {
   "post": {
    "operationId": "identities.listReports",
    "tags": [
     "identities"
    ],
    "summary": "Lists the active people whose manager is this identity, by name.",
    "description": "- **Permission:** `iam:identities:read` on the identity.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant.\n\nDisabled and deleted reports are left out. Managers are set with `managerId` on [`create`](#create) or\n[`update`](#update), and they can approve requests for eligible bindings and access packages that ask for manager\napproval (see [approver groups and managers](/docs/guides/privileged-access/elevation#approver-groups-and-managers)).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#listreports"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicIdentity"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/listSessions": {
   "post": {
    "operationId": "identities.listSessions",
    "tags": [
     "identities"
    ],
    "summary": "Lists an identity's unexpired sessions and API keys, most recently used first, without token hashes.",
    "description": "- **Permission:** `iam:identities:read` on the identity.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant.\n\nUse it for device lists and support. Each session shows when it was created, last used, and expires, how it was\nestablished (`method`), whether it completed MFA, the client details recorded at sign-in, and `impersonatorId` for\n\"view as\" sessions. End them with [`revokeSessions`](#revokesessions).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#listsessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "identityId": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "expiresAt": {
              "type": "number"
             },
             "lastSeenAt": {
              "type": "number"
             },
             "authenticatedAt": {
              "type": "number"
             },
             "mfa": {
              "type": "boolean"
             },
             "kind": {
              "type": "string",
              "enum": [
               "user",
               "role",
               "api-key",
               "session-token",
               "delegated"
              ]
             },
             "agentId": {
              "type": "string"
             },
             "delegationId": {
              "type": "string"
             },
             "mfaAuthenticatedAt": {
              "type": "number"
             },
             "credentialAuthorityId": {
              "type": "string"
             },
             "sessionName": {
              "type": "string"
             },
             "sourceIdentity": {
              "type": "string"
             },
             "sessionTags": {
              "type": "object",
              "properties": {},
              "additionalProperties": {
               "type": "string"
              }
             },
             "sourcePolicy": {
              "$ref": "#/components/schemas/PolicyDocument"
             },
             "format": {
              "type": "string",
              "enum": [
               "jwt"
              ]
             },
             "audience": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "webIdentity": {
              "type": "object",
              "properties": {
               "providerId": {
                "type": "string"
               },
               "issuer": {
                "type": "string"
               },
               "subject": {
                "type": "string"
               }
              },
              "required": [
               "providerId",
               "issuer",
               "subject"
              ],
              "additionalProperties": false
             },
             "method": {
              "type": "string",
              "enum": [
               "password",
               "passwordless-email",
               "passwordless-sms",
               "passkey",
               "federated",
               "impersonation"
              ]
             },
             "client": {
              "$ref": "#/components/schemas/SessionClientInfo"
             },
             "name": {
              "type": "string"
             },
             "description": {
              "type": "string"
             },
             "impersonatorId": {
              "type": "string"
             },
             "impersonatorSessionId": {
              "type": "string"
             },
             "trustedDeviceId": {
              "type": "string"
             },
             "originalIdentityId": {
              "type": "string"
             },
             "sourceTenantId": {
              "type": "string"
             },
             "roleId": {
              "type": "string"
             },
             "trustId": {
              "type": "string"
             },
             "sourceSessionId": {
              "type": "string"
             },
             "sourceAuthorityIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "policy": {
              "$ref": "#/components/schemas/PolicyDocument"
             },
             "previousSignIn": {
              "$ref": "#/components/schemas/SignInRecord"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             }
            },
            "required": [
             "identityId",
             "createdAt",
             "expiresAt",
             "lastSeenAt",
             "authenticatedAt",
             "mfa",
             "kind",
             "id",
             "tenantId"
            ],
            "additionalProperties": {}
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/offboard": {
   "post": {
    "operationId": "identities.offboard",
    "tags": [
     "identities"
    ],
    "summary": "Disables an identity and removes everything that gave it access in one transaction, handing what it owned to a successor.",
    "description": "- **Permission:** `iam:identities:update` on the identity, with recent authentication. Offboarding an owner also\n  requires you to be an owner of this tenant, signed in to it with your own account, or root. Direct bindings and\n  group memberships are removed as `bindings.delete` and `groups.removeMember` would, under your grant authority.\n- **Audited as:** `iam:identities:update` and `identity:offboard` (with the reason, kind, successor, and every\n  count).\n- **Errors:** `INVALID_INPUT` for yourself, a missing reason, or a successor who is the same identity or not\n  active; `ACCESS_DENIED` for a root administrator unless you are root, for an owner unless you are an owner or\n  root, or for a binding or group membership issued under another administrator's grant authority; `LAST_OWNER` or\n  `LAST_ROOT_ADMIN`; `NOT_FOUND` for an unknown or deleted identity or successor; `RECENT_AUTH_REQUIRED`.\n\nIn order, it removes ownership (the protected Owner binding); ends role activations; revokes\n[access-package](/docs/guides/privileged-access/access-packages) assignments with the bindings and memberships\nthey created; deletes the remaining direct bindings and group memberships; deletes relationships; cancels pending\naccess and package requests; revokes the grant authorities the identity holds, so grants it issued as a delegated\nadministrator stop applying; moves its reports to the successor; transfers the managed resources it owns to the\nsuccessor; ends every session and API key; and disables it. Without a successor, reports are left without a\nmanager and owned resources are only counted (`resourcesOwned`). The result counts each step, which makes a good\nrecord for auditors.\n\nThe identity stays as a disabled record so the audit trail still names who they were; remove it later with\n[`delete`](#delete). Package rules owned by the leaver are suspended when their authority is revoked, so hand them\nover first. See [offboarding](/docs/guides/privileged-access/lifecycle#offboarding).\n\n```ts\nconst summary = await iam.api.identities.offboard(credential, {\n  tenantId,\n  identityId: leaverId,\n  reason: 'Left the company (HR-1234)',\n  successorId: managerId,\n});\n// summary.bindings, summary.memberships, summary.resourcesReassigned, ...\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#offboard"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         },
         "successorId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "departmentsReassigned": {
             "type": "number"
            },
            "teamsLeft": {
             "type": "number"
            },
            "delegationsRevoked": {
             "type": "number"
            },
            "agentsUnsponsored": {
             "type": "number"
            },
            "agentsReassigned": {
             "type": "number"
            },
            "sessions": {
             "type": "number"
            },
            "bindings": {
             "type": "number"
            },
            "memberships": {
             "type": "number"
            },
            "activations": {
             "type": "number"
            },
            "packages": {
             "type": "number"
            },
            "relationships": {
             "type": "number"
            },
            "accessRequests": {
             "type": "number"
            },
            "authorities": {
             "type": "number"
            },
            "resourcesReassigned": {
             "type": "number"
            },
            "resourcesOwned": {
             "type": "number"
            },
            "reportsReassigned": {
             "type": "number"
            },
            "identity": {
             "$ref": "#/components/schemas/PublicIdentity"
            }
           },
           "required": [
            "sessions",
            "bindings",
            "memberships",
            "activations",
            "packages",
            "relationships",
            "accessRequests",
            "authorities",
            "resourcesReassigned",
            "resourcesOwned",
            "reportsReassigned",
            "identity"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/requestPasswordReset": {
   "post": {
    "operationId": "identities.requestPasswordReset",
    "tags": [
     "identities"
    ],
    "summary": "Emails a member a password-reset link on an administrator's behalf.",
    "description": "- **Permission:** `iam:identities:update` on the member, with recent authentication. For an owner you must be\n  another owner of the same tenant or root; for a root administrator, root.\n- **Audited as:** `iam:identities:update` and `identity:password-reset`.\n- **Errors:** `FEATURE_DISABLED` when email delivery or password sign-in is not configured; `INVALID_INPUT` for a\n  service account, a disabled identity, or one without an email; `ACCESS_DENIED` for an owner or root\n  administrator you may not control; `RECENT_AUTH_REQUIRED`.\n\nUnlike the public `auth.requestPasswordReset`, it works whether or not the address is verified, which makes it the\nway to onboard someone created without a password. It returns `{ queued: true, email }`; the reset token goes only\nto the member's inbox. Whoever controls a password reset controls the account, so the owner and root rules keep\n`iam:identities:update` alone from taking over a more powerful account. See\n[recovery](/docs/guides/authentication/recovery#resetting-on-someones-behalf).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#requestpasswordreset"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "queued": {
             "type": "boolean"
            },
            "email": {
             "type": "string"
            }
           },
           "required": [
            "queued",
            "email"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/resendInvitation": {
   "post": {
    "operationId": "identities.resendInvitation",
    "tags": [
     "identities"
    ],
    "summary": "Sends a member invitation again with a new token and a fresh lifetime; the earlier link stops working.",
    "description": "- **Permission:** `iam:identities:update` on the invitation.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `CONFLICT` when the invitation was already accepted or revoked; `DELIVERY_REQUIRED` without an email\n  delivery callback; `NOT_FOUND` when the invitation is not in this tenant.\n\nUse it when the first email expired, was lost, or went to spam: expired invitations can be resent. The new email\nnames you as the inviter, while the invitation keeps its original inviter, roles, groups, and grant authority.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#resendinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invitationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "invitationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "invitationId": {
             "type": "string"
            },
            "email": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            }
           },
           "required": [
            "invitationId",
            "email",
            "expiresAt"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/revokeInvitation": {
   "post": {
    "operationId": "identities.revokeInvitation",
    "tags": [
     "identities"
    ],
    "summary": "Cancels a member invitation so its link can no longer be used.",
    "description": "- **Permission:** `iam:identities:update` on the invitation.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `CONFLICT` when the invitation was already accepted or revoked; `NOT_FOUND` when it is not in this\n  tenant.\n\nThe invitation stays in [`listInvitations`](#listinvitations) with `revoked: true`, and a revoked invitation cannot\nbe re-sent.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#revokeinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invitationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "invitationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "boolean"
            },
            "email": {
             "type": "string"
            },
            "name": {
             "type": "string"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "groupIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "authorityId": {
             "type": "string"
            },
            "inviterId": {
             "type": "string"
            },
            "createdAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "consumed": {
             "type": "boolean"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            }
           },
           "required": [
            "revoked",
            "email",
            "roleIds",
            "groupIds",
            "inviterId",
            "createdAt",
            "expiresAt",
            "consumed",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/revokeSessions": {
   "post": {
    "operationId": "identities.revokeSessions",
    "tags": [
     "identities"
    ],
    "summary": "Ends every session and API key of an identity without disabling it.",
    "description": "- **Permission:** `iam:identities:update` on the identity, with recent authentication.\n- **Audited as:** `iam:identities:update` and `identity:revoke-sessions` (with the number revoked).\n- **Errors:** `ACCESS_DENIED` for a root administrator unless you are root; `NOT_FOUND` when the identity is not in\n  this tenant; `RECENT_AUTH_REQUIRED`.\n\nUse it for incident response or a lost device. Remembered devices and pending sign-in challenges are cleared too,\nso the next sign-in needs the second factor again, and role sessions assumed from the identity and \"view as\"\nsessions opened through its sessions end as well. The account and its access stay, so a person can sign in again\nat once. For a service account this deletes its API keys; issue new ones with\n[`credentials.create`](/docs/reference/api/credentials#create). The result's `revoked` is the number of session\nrecords the identity held.\n\nPass `keepApiKeys: true` to end everything except the API keys: user sessions, role sessions the identity assumed in\nother tenants, session tokens (including those minted from its keys), remembered devices, and pending challenges end,\nwhile the keys keep working. Use it when a service account's session tokens may have leaked but its keys have not.\nThe audit event records `keptApiKeys`. A value other than `true` or `false` is `INVALID_INPUT`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#revokesessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "keepApiKeys": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/setBoundary": {
   "post": {
    "operationId": "identities.setBoundary",
    "tags": [
     "identities"
    ],
    "summary": "Sets a root-controlled permissions boundary on one identity, capping what it may do in the tenant whatever its roles grant.",
    "description": "- **Permission:** `iam:boundaries:update` on the identity, and you must be a root administrator.\n- **Audited as:** `iam:boundaries:update`.\n- **Errors:** `ACCESS_DENIED` for anyone but root; `INVALID_POLICY`, `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE`\n  for a document the catalog rejects; `NOT_FOUND` when the identity is not in this tenant; `INVARIANT_VIOLATION`\n  when the change would break an enforced access invariant.\n\nA <Term id=\"boundary\">boundary</Term> never grants: an action is allowed only when a role grants it and the\nboundary allows it too, and boundaries set on the tenant and its ancestors apply on top. Each identity has at most\none boundary per tenant, and calling again replaces it. Boundaries are platform controls, which is why tenant\nadministrators cannot set them. See [boundaries](/docs/guides/authorization/policies#boundaries).\n\n```ts\n// A vendor account may never reach beyond support tickets, whatever roles it is given.\nawait iam.api.identities.setBoundary(rootCredential, {\n  tenantId,\n  identityId: vendorId,\n  document: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['tickets:*'], resources: ['*'] }],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#setboundary"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "document": {
          "$ref": "#/components/schemas/PolicyDocument"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "document"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            },
            "identityId": {
             "type": "string"
            },
            "document": {
             "$ref": "#/components/schemas/PolicyDocument"
            }
           },
           "required": [
            "id",
            "tenantId",
            "uniqueKey",
            "identityId",
            "document"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/setOwner": {
   "post": {
    "operationId": "identities.setOwner",
    "tags": [
     "identities"
    ],
    "summary": "Makes a member an owner of the tenant, or removes their ownership.",
    "description": "- **Permission:** `iam:identities:update` on the member, with recent authentication, and you must be an owner of\n  this tenant yourself (signed in to it with your own account, not through an assumed role) or root.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `ACCESS_DENIED` when you are not an owner or root; `INVALID_INPUT` for a service account, a disabled\n  member, or a non-boolean `owner`; `LAST_OWNER` when removing the last active owner; `RECENT_AUTH_REQUIRED`.\n\nOwnership is the protected [Owner role](/docs/guides/authorization/roles#the-owner-role), which allows every action\nin the tenant and cannot be bound, edited, or requested any other way. Granting it binds the Owner role under a new,\nunrestricted grant authority delegated from the one you grant under, so the new owner can administer and delegate\nlike you, within your authority chain; if an authority above theirs is revoked (offboarding or deleting you\nrevokes yours), their grants stop applying. Removing ownership deletes the Owner binding but leaves the person's\ngrant authorities, so grants they issued keep applying; revoke those with\n[`authorities.revoke`](/docs/reference/api/authorities#revoke) if they should not.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#setowner"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "owner": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "owner"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/setStatus": {
   "post": {
    "operationId": "identities.setStatus",
    "tags": [
     "identities"
    ],
    "summary": "Disables an identity or re-enables it.",
    "description": "- **Permission:** `iam:identities:update` on the identity, with recent authentication.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `INVALID_INPUT` for a status other than `active` or `disabled`; `ACCESS_DENIED` for a root\n  administrator unless you are root; `LAST_OWNER` or `LAST_ROOT_ADMIN` when disabling the last active owner or\n  root administrator; `INVALID_TRANSITION` (409) when re-enabling an identity whose `expiresAt` has passed;\n  `NOT_FOUND` for a deleted identity; `INVARIANT_VIOLATION` when the change would break an enforced access\n  invariant.\n\nDisabling ends every session and API key at once, and the identity can no longer sign in or authenticate. Its\nroles, groups, and attributes are kept and apply again when you re-enable it, but ended sessions and keys do not\ncome back. It works for people and service accounts alike\n([`serviceAccounts.setStatus`](/docs/reference/api/service-accounts#setstatus) is the service-account\nequivalent). Access-package rules are reconciled after the change and never assign anything to a disabled\nidentity. To remove access for good, use [`offboard`](#offboard).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#setstatus"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "active",
           "disabled"
          ]
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "status"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/unlock": {
   "post": {
    "operationId": "identities.unlock",
    "tags": [
     "identities"
    ],
    "summary": "Clears the rate-limit counters that lock a person out of sign-in, recovery, and MFA after too many attempts.",
    "description": "- **Permission:** `iam:identities:update` on the identity, with recent authentication.\n- **Audited as:** `iam:identities:update` and `identity:unlock` (with `supported` and `cleared`).\n- **Errors:** `RECENT_AUTH_REQUIRED`; `NOT_FOUND` for an unknown or deleted identity.\n\nIt resets the counters kept for the identity's email, phone, and ID across sign-in, sign-up, re-authentication,\nemail verification and change, phone verification, password reset and change, passwordless, passkey, and\nsecond-factor flows. Counters kept per client address and\n[network blocks](/docs/reference/api/security#unblocknetwork) are not affected. A custom limiter without a `reset`\nmethod returns `{ supported: false, cleared: 0 }`; otherwise `cleared` is the number of counters reset. See\n[lockouts](/docs/guides/authentication/recovery#lockouts).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#unlock"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "supported": {
             "type": "boolean"
            },
            "cleared": {
             "type": "number"
            }
           },
           "required": [
            "supported",
            "cleared"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/identities/update": {
   "post": {
    "operationId": "identities.update",
    "tags": [
     "identities"
    ],
    "summary": "Changes an identity's name, declared attributes, email, manager, or scheduled deactivation.",
    "description": "- **Permission:** `iam:identities:update` on the identity. An email change also needs recent authentication, and\n  for an owner or root administrator the same control as [`requestPasswordReset`](#requestpasswordreset); changing\n  a root administrator's expiry needs root.\n- **Audited as:** `iam:identities:update`, plus `identity:email-change` (with the old and new address) when the\n  email changes.\n- **Errors:** `INVALID_INPUT` when no field is given, for an undeclared or mistyped attribute, an `expiresAt` that\n  is not in the future or is more than ten years ahead, an email on a service account, or a manager who is the\n  identity itself, is not active, or reports to the identity (directly or further down); `IDENTITY_EXISTS` when the\n  new email is taken in this tenant; `LAST_OWNER` when setting an expiry on the last active owner;\n  `ACCESS_DENIED` for a protected account you may not change; `NOT_FOUND` for a deleted identity;\n  `INVARIANT_VIOLATION` when the change would break an enforced access invariant.\n\nOnly the fields you pass change. `attributes` replaces the whole set of declared attributes, validated against\n`permissions.identityAttributes`. A new email is marked unverified and every session ends, because whoever\ncontrols the sign-in address controls the account. `expiresAt: null` clears a scheduled deactivation and\n`managerId: null` removes the manager; to end access immediately, disable the identity instead of setting an\nexpiry. After the change, automatic access-package rules are reconciled for the identity, so new attribute values\ncan change which packages it receives.\n\n```ts\nawait iam.api.identities.update(credential, {\n  tenantId,\n  identityId,\n  attributes: { department: 'finance', level: 3 },\n  managerId: newManagerId,\n  expiresAt: null, // no longer a temporary account\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/identities#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "attributes": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "description": "Any JSON value"
          }
         },
         "email": {
          "type": "string"
         },
         "expiresAt": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         },
         "managerId": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/impact/preview": {
   "post": {
    "operationId": "impact.preview",
    "tags": [
     "impact"
    ],
    "summary": "Simulates a role update, a policy document change, or a role deletion, and reports the actions each holder would gain and lose per resource, plus the access invariants the change would break or fix.",
    "description": "- **Permission:** `iam:policies:simulate` on the tenant, plus what the real change needs: `iam:roles:update` or\n  `iam:roles:delete` on the role, or `iam:policies:update` on the policy, and the edit rights of the grant\n  authority that created it (or root).\n- **Audited as:** `iam:policies:simulate`. The simulated change is not audited, because it never happens.\n- **Errors:** `INVALID_INPUT` when `change` does not name exactly one of `role`, `policy`, or `deleteRole`, when\n  `resources` does not hold 1 to 10 entries, or when `actions` does not hold 1 to 200; `INVALID_ACTION` for an\n  action missing from the catalog; `ACCESS_DENIED` when you lack the permission or edit rights the change needs;\n  `IMPERSONATION_RESTRICTED` from a \"view as\" session; `NOT_FOUND` when the role, policy, or a managed resource does\n  not exist; and any error the real call would raise, such as `RESOURCE_IN_USE` for deleting a role that others\n  inherit, `PROTECTED_RESOURCE`, or `INVALID_POLICY`.\n\n`change` takes one of three shapes: `{ role: { roleId, ...update } }` with the fields\n[`roles.update`](/docs/reference/api/roles#update) accepts, `{ policy: { policyId, document } }` for a new policy\ndocument, or `{ deleteRole: roleId }`. `assumeMfa: true` evaluates holders as MFA-verified.\n\nThe result lists the affected `roles`, the number of holders `evaluated` (with `truncated: true` when more than\n200 were skipped), and `identities`: only the holders whose access changes, each with `changes` per resource\n(`gained` and `lost` action names). `gainedTotal` and `lostTotal` sum them up, and `invariants` lists those the\nchange would newly break (`broken`, with the new violations) or make pass again (`fixed`).\n\n```ts\nconst preview = await iam.api.impact.preview(credential, {\n  tenantId,\n  change: { role: { roleId: approver.id, permissions: ['payments:read', 'payments:approve'] } },\n  resources: [{ type: 'ledger', id: 'main' }],\n});\n// preview.identities: [{ identity: { id, name }, changes: [{ resource: 'ledger/main', gained: [...], lost: [...] }] }]\n// preview.invariants.broken: guardrails the change would break\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/impact#preview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "change": {
          "$ref": "#/components/schemas/ImpactChange"
         },
         "resources": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "type": {
             "type": "string"
            },
            "id": {
             "type": "string"
            }
           },
           "required": [
            "type",
            "id"
           ],
           "additionalProperties": false
          }
         },
         "actions": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "assumeMfa": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "change",
         "resources"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ImpactPreview"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/check": {
   "post": {
    "operationId": "inference.check",
    "tags": [
     "inference"
    ],
    "summary": "Tells the caller whether they may invoke a model now and, when they may, returns a single-use ticket for an external gateway.",
    "description": "- **Permission:** None: any credential, for itself. The model decision is `inference:invoke` on `model/{name}`.\n- **Audited as:** Denials as `inference:invoke` with outcome `deny`, like any decision; the first budget refusal in a\n  window as `inference:budget-exceeded`.\n- **Errors:** `NOT_FOUND` for a model the tenant cannot see; `INVALID_INPUT` for a malformed model name, an\n  `estimatedTokens` outside 0 to 100 000 000, or `tools` that are not at most 64 provider tool ids;\n  `FEATURE_DISABLED` without the `inference` option.\n\nThe answer is `{ allowed: true, model, budgets, ticket }`, or `{ allowed: false, reason, model }`. The `reason` is one\nof:\n\n- `ACCESS_DENIED`;\n- `MODEL_DISABLED`: the model or its provider is off;\n- `TOOL_NOT_ALLOWED`: with the refused `tool`, when the model's `providerTools` does not allow one of `tools`;\n- `BUDGET_EXCEEDED`: with the exhausted `budget`'s standing.\n\n`tools` lists the provider-run tools the call will ask for, as ids such as `web_search` or `mcp:kb.acme.com`\n(`providerToolsOf` from `better-iam` reads them from a request body). `estimatedTokens`, priced at the model's input\nprice, must fit in every covering budget. The\n`ticket` is valid for one hour and names the caller, their session, and the model: an external gateway makes the call\nand then redeems it with `record`.\n\n```ts\nconst result = await iam.api.inference.check(credential, { tenantId, model: 'opus', estimatedTokens: 4_000 });\nif (!result.allowed) return refuse(result.reason);\n// Call the provider, then report the tokens with result.ticket through inference.record.\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#check"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "model": {
          "type": "string"
         },
         "estimatedTokens": {
          "type": "number"
         },
         "tools": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId",
         "model"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "anyOf": [
            {
             "allOf": [
              {
               "type": "object",
               "properties": {
                "allowed": {
                 "type": "boolean",
                 "const": true
                },
                "model": {
                 "$ref": "#/components/schemas/PublicModel"
                },
                "budgets": {
                 "type": "array",
                 "items": {
                  "$ref": "#/components/schemas/BudgetStanding"
                 }
                }
               },
               "required": [
                "allowed",
                "model",
                "budgets"
               ],
               "additionalProperties": false
              },
              {
               "type": "object",
               "properties": {
                "ticket": {
                 "type": "string"
                }
               },
               "additionalProperties": false
              }
             ]
            },
            {
             "allOf": [
              {
               "type": "object",
               "properties": {
                "allowed": {
                 "type": "boolean",
                 "const": false
                },
                "reason": {
                 "type": "string",
                 "enum": [
                  "ACCESS_DENIED",
                  "BUDGET_EXCEEDED",
                  "MODEL_DISABLED",
                  "TOOL_NOT_ALLOWED"
                 ]
                },
                "model": {
                 "$ref": "#/components/schemas/PublicModel"
                },
                "budget": {
                 "$ref": "#/components/schemas/BudgetStanding"
                },
                "tool": {
                 "type": "string",
                 "description": "For TOOL_NOT_ALLOWED: the first provider tool refused."
                }
               },
               "required": [
                "allowed",
                "reason"
               ],
               "additionalProperties": false
              },
              {
               "type": "object",
               "properties": {
                "ticket": {
                 "type": "string"
                }
               },
               "additionalProperties": false
              }
             ]
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/createModel": {
   "post": {
    "operationId": "inference.createModel",
    "tags": [
     "inference"
    ],
    "summary": "Publishes a model under a public name, served by a provider of this tenant or an ancestor.",
    "description": "- **Permission:** `iam:inference:manage` on the tenant.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `CONFLICT` (409) when this tenant already has a model of that name; `NOT_FOUND` when the provider is not\n  this tenant's or an ancestor's; `INVALID_INPUT` for a malformed name, a `family` or `tier` that is not a short\n  identifier, a `contextWindow` or `maxOutputTokens` outside 1 to 100 000 000, a price outside 0 to 10 000,\n  `fallbacks` naming more than five models or the model itself, or a `providerTools` other than `allow`, `deny`, or\n  `policy`.\n\nThe `name` (1 to 128 letters, digits, or `._:/@+-`, starting with a letter or digit) is what callers send and what\npolicies name as `model/{name}`; `upstreamModel` is the provider's own name for it. Prices are US dollars per million\ntokens and drive cost metering and cost budgets; a model without prices costs nothing. `tier` and `family` are\nfree-form attributes for policies. `maxOutputTokens` caps the output of every gateway call. `fallbacks` lists models\nthe gateway tries, in order, when this one's provider cannot be reached or answers 429, 500, 502, 503, 504, or 529;\neach one only if the caller may call it and it speaks the same wire format. `providerTools` (`allow` by default,\n`deny`, or `policy`) governs the tools the provider runs itself (see\n[Models, inheritance, and policies](#models-inheritance-and-policies)). A new model starts enabled, and sub-tenants\ninherit it.\n\n```ts\nawait iam.api.inference.createModel(credential, {\n  tenantId,\n  name: 'opus',\n  providerId: anthropic.id,\n  upstreamModel: 'claude-opus-5-5',\n  tier: 'frontier',\n  family: 'claude',\n  inputPricePerMTok: 5,\n  outputPricePerMTok: 25,\n  cachedInputPricePerMTok: 0.5,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#createmodel"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/ModelInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "name": {
            "type": "string"
           },
           "providerId": {
            "type": "string"
           },
           "upstreamModel": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "name",
           "providerId",
           "upstreamModel"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicModel"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/createProvider": {
   "post": {
    "operationId": "inference.createProvider",
    "tags": [
     "inference"
    ],
    "summary": "Registers an upstream model provider account, whose API key is sealed and never returned.",
    "description": "- **Permission:** `iam:inference:manage` on the tenant, and a recent sign-in.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `CONFLICT` (409) for a name another provider of the tenant uses (ignoring case); `ACCESS_DENIED` for a\n  custom `baseUrl` from anyone but a root administrator, unless the deployment sets `inference.allowCustomBaseUrls`;\n  `INVALID_INPUT` for an unknown `kind`, an `openai-compatible` provider without `baseUrl`, an `apiKey` that is not 8\n  to 4096 characters without whitespace, or a `baseUrl` that is not https or carries credentials, a query, or a\n  fragment; `RECENT_AUTH_REQUIRED` without a recent sign-in.\n\n`kind` is `anthropic`, `openai`, or `openai-compatible` (any endpoint that speaks the OpenAI Chat Completions API).\nThe first two default to the provider's public endpoint. A custom base URL makes the gateway send the key to that\naddress, hence the root rule. The key is sealed with the deployment secret, bound to this provider, and opened only\ninside the server; the result shows `keyHint`, its last four characters. Models of this tenant and its sub-tenants may\nuse the provider. [`rotateSecrets`](/docs/reference/api#rotatesecrets) re-seals provider keys under a new secret.\n\n```ts\nconst anthropic = await iam.api.inference.createProvider(credential, {\n  tenantId,\n  name: 'Anthropic',\n  kind: 'anthropic',\n  apiKey: process.env.ANTHROPIC_API_KEY!,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#createprovider"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "kind": {
          "$ref": "#/components/schemas/ProviderKind"
         },
         "apiKey": {
          "type": "string"
         },
         "baseUrl": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name",
         "kind",
         "apiKey"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicProvider"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/deleteBudget": {
   "post": {
    "operationId": "inference.deleteBudget",
    "tags": [
     "inference"
    ],
    "summary": "Deletes a budget together with its usage counters.",
    "description": "- **Permission:** `iam:inference:manage` on the budget.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `NOT_FOUND` when the budget is not in this tenant.\n\nCalls it covered are no longer capped by it. Usage records are kept, so reports still show the spending.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#deletebudget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "budgetId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "budgetId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/deleteModel": {
   "post": {
    "operationId": "inference.deleteModel",
    "tags": [
     "inference"
    ],
    "summary": "Removes one of this tenant's models; an inherited model of the same name becomes visible again.",
    "description": "- **Permission:** `iam:inference:manage` on the tenant.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `NOT_FOUND` when this tenant defines no model of that name (an inherited model is deleted in the tenant\n  that defines it).\n\nCallers asking for the name afterwards get `NOT_FOUND` unless an ancestor defines it. To stop calls without deleting,\nuse `updateModel` with `enabled: false`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#deletemodel"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/deleteProvider": {
   "post": {
    "operationId": "inference.deleteProvider",
    "tags": [
     "inference"
    ],
    "summary": "Deletes a provider that no model uses, together with its sealed key.",
    "description": "- **Permission:** `iam:inference:manage` on the provider.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `RESOURCE_IN_USE` (409) while any model uses it, a sub-tenant's included; `NOT_FOUND` when the provider\n  is not this tenant's own.\n\nPoint the models at another provider with `updateModel`, or delete them, first.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#deleteprovider"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "providerId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "providerId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/listBudgets": {
   "post": {
    "operationId": "inference.listBudgets",
    "tags": [
     "inference"
    ],
    "summary": "Lists the tenant's budgets by name, with the current window's standing of each shared pool.",
    "description": "- **Permission:** `iam:inference:read` on the tenant.\n- **Audited as:** `iam:inference:read`.\n\nEach budget carries `maxCostUsd` next to the stored `maxCostMicros`, `subjectName` (the group's name, or the\nidentity's email or name), and, for `shared` and `identity` budgets, `standing`: the window's start, `resetsAt`, and\nthe tokens and cost used and remaining. An `each` budget has one pool per identity; people see theirs with `myUsage`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#listbudgets"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/InferenceBudgetView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/listMine": {
   "post": {
    "operationId": "inference.listMine",
    "tags": [
     "inference"
    ],
    "summary": "Lists the enabled models the caller may invoke, for a model picker.",
    "description": "- **Permission:** None: any credential, for itself.\n- **Audited as:** Not audited; the decisions are evaluated without being recorded.\n- **Errors:** `FEATURE_DISABLED` without the `inference` option.\n\nEach model is decided like an `inference:invoke` call, so a delegated agent session sees only what the person may use\nwithin the delegation. Budgets are not considered; `check` does that before a call.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicModel"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/listModels": {
   "post": {
    "operationId": "inference.listModels",
    "tags": [
     "inference"
    ],
    "summary": "Lists every model the tenant can see, its own and inherited ones, by name.",
    "description": "- **Permission:** `iam:inference:read` on the tenant.\n- **Audited as:** `iam:inference:read`.\n\nWhen a tenant and an ancestor define the same name, only the nearest definition appears. `inherited` marks models an\nancestor defines, and `enabled` is false when the model is disabled or its provider is gone. `provider` names the\nprovider and its kind; keys never appear.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#listmodels"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicModel"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/listProviders": {
   "post": {
    "operationId": "inference.listProviders",
    "tags": [
     "inference"
    ],
    "summary": "Lists the providers this tenant's models may use: its own, then its ancestors'.",
    "description": "- **Permission:** `iam:inference:read` on the tenant.\n- **Audited as:** `iam:inference:read`.\n\nThe tenant's own providers come first (`inherited: false`), each group sorted by name. Each shows `kind`, `baseUrl`,\n`keyHint` (the key's last four characters), and `keyRotatedAt`, when the key was last replaced.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#listproviders"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicProvider"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/myUsage": {
   "post": {
    "operationId": "inference.myUsage",
    "tags": [
     "inference"
    ],
    "summary": "Returns the caller's own usage by model and the standing of every budget that covers them.",
    "description": "- **Permission:** None: any credential, for itself, in its own tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `ACCESS_DENIED` when `tenantId` is not the credential's own tenant; `INVALID_INPUT` when `from` is after\n  `to`.\n\nThe report covers `from` (by default the start of this month, UTC) to `to` (by default now). `budgets` lists the\nstanding of every budget that covers the caller for a model they used in that range or can see, so people can check\nwhat is left of an allowance before they run out.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#myusage"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "from": {
          "type": "number"
         },
         "to": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/UsageReport"
            },
            {
             "type": "object",
             "properties": {
              "budgets": {
               "type": "array",
               "items": {
                "$ref": "#/components/schemas/BudgetStanding"
               }
              }
             },
             "required": [
              "budgets"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/record": {
   "post": {
    "operationId": "inference.record",
    "tags": [
     "inference"
    ],
    "summary": "Meters a call an external gateway made after check, by redeeming the check's ticket.",
    "description": "- **Permission:** `iam:inference:record` on the tenant, typically held by the gateway's service account.\n- **Audited as:** `iam:inference:record`; crossing a budget's `alertAtPercent` also as `inference:budget-alert`.\n- **Errors:** `INVALID_TICKET` (400) for an unknown, used, expired, or other-tenant ticket; `INVALID_INPUT` for a token\n  count outside 0 to 100 000 000.\n\nThe ticket supplies the caller as the check saw them (their identity, and the agent and delegation behind a delegated\nsession) and the model; the gateway reports the token counts from the provider's response (`inputTokens`,\n`outputTokens`, and optionally `cacheReadTokens` and `cacheWriteTokens`), plus `status` (`error` for a failed call),\n`requestId`, and `latencyMs`. The usage is attributed to that caller and added to every covering budget and delegation\nspending cap, even when the caller's session has ended since the check, so a short session cannot make an allowed call\nescape its budgets. Each ticket works once. The result is\n`{ recorded: true, costMicros }`.\n\n```ts\nawait iam.api.inference.record(gatewayKey, {\n  tenantId,\n  ticket: result.ticket,\n  inputTokens: response.usage.input_tokens,\n  outputTokens: response.usage.output_tokens,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#record"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/InferenceTokenUsage"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "ticket": {
            "type": "string"
           },
           "status": {
            "type": "string",
            "enum": [
             "ok",
             "error"
            ]
           },
           "requestId": {
            "type": "string"
           },
           "latencyMs": {
            "type": "number"
           }
          },
          "required": [
           "tenantId",
           "ticket"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "recorded": {
             "type": "boolean",
             "const": true
            },
            "costMicros": {
             "type": "number"
            }
           },
           "required": [
            "recorded",
            "costMicros"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/setBudget": {
   "post": {
    "operationId": "inference.setBudget",
    "tags": [
     "inference"
    ],
    "summary": "Creates a budget, or replaces one when budgetId is given.",
    "description": "- **Permission:** `iam:inference:manage` on the tenant.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `CONFLICT` (409) for a name another budget of the tenant uses (ignoring case); `NOT_FOUND` for a group\n  or identity that is not in this tenant (or a deleted identity), or a `budgetId` that is not; `INVALID_INPUT` without\n  any of `maxTokens`, `maxCostUsd` and `maxRequests`, for a `maxCostUsd` that is not above 0 or is above 10 000, a\n  `maxTokens` or `maxRequests` below 1, an `alertAtPercent` outside 1 to 100, or a malformed model pattern.\n\n`subjectId` names the group or identity; a `tenant` budget covers the tenant itself. `maxRequests` caps the number of\ncalls per window, a rate limit for agents that loop. With `budgetId`, fields you leave out keep their stored values,\nand `null` clears `maxTokens`, `maxCostUsd`, `maxRequests`, `models`, or `alertAtPercent`. `alertAtPercent` audits\n`inference:budget-alert` once per window when usage crosses that share of any limit.\n\n```ts\n// Every person gets a million tokens a day.\nawait iam.api.inference.setBudget(credential, {\n  tenantId,\n  name: 'Daily per person',\n  subjectType: 'tenant',\n  scope: 'each',\n  period: 'day',\n  maxTokens: 1_000_000,\n  alertAtPercent: 80,\n});\n// One agent, across its own key and every delegated session.\nawait iam.api.inference.setBudget(credential, {\n  tenantId,\n  name: 'Triage agent',\n  subjectType: 'identity',\n  subjectId: agentId,\n  period: 'month',\n  maxCostUsd: 200,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#setbudget"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/InferenceBudgetInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "budgetId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/InferenceBudgetView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/updateModel": {
   "post": {
    "operationId": "inference.updateModel",
    "tags": [
     "inference"
    ],
    "summary": "Changes one of this tenant's models, found by its name.",
    "description": "- **Permission:** `iam:inference:manage` on the tenant.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `NOT_FOUND` when this tenant defines no model of that name or the new provider is not visible to it;\n  `INVALID_INPUT` as for `createModel`.\n\nFields you leave out keep their values, and `null` clears an optional one. `enabled: false` stops every call to the\nmodel at once (checks answer `MODEL_DISABLED`); new prices apply to calls metered from then on. The name cannot change.\nAn inherited model changes only in the tenant that defines it; a sub-tenant publishes its own model of the same name\ninstead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#updatemodel"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/ModelInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "name": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "name"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicModel"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/updateProvider": {
   "post": {
    "operationId": "inference.updateProvider",
    "tags": [
     "inference"
    ],
    "summary": "Renames a provider, moves its base URL, or replaces its API key.",
    "description": "- **Permission:** `iam:inference:manage` on the provider, and a recent sign-in.\n- **Audited as:** `iam:inference:manage`.\n- **Errors:** `NOT_FOUND` when the provider is not this tenant's own; `CONFLICT` (409) for a name another provider\n  uses; `ACCESS_DENIED` and `INVALID_INPUT` for `baseUrl` and `apiKey` as in `createProvider`;\n  `RECENT_AUTH_REQUIRED` without a recent sign-in.\n\nA new `apiKey` is sealed like the first one and updates `keyHint` and `keyRotatedAt`; the next call through the\ngateway uses it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#updateprovider"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "providerId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "apiKey": {
          "type": "string"
         },
         "baseUrl": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "providerId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicProvider"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/inference/usage": {
   "post": {
    "operationId": "inference.usage",
    "tags": [
     "inference"
    ],
    "summary": "Reports model calls between two times, grouped by identity, agent, model, or day.",
    "description": "- **Permission:** `iam:inference:read` on the tenant.\n- **Audited as:** `iam:inference:read`.\n- **Errors:** `INVALID_INPUT` for an unknown `groupBy` or a `from` after `to`.\n\n`from` defaults to the start of this month (UTC) and `to` to now; `identityId`, `agentId`, and `model` narrow the\nrecords, and `groupBy` defaults to `model`. Each row counts requests, errors, input, output, and cache tokens, and cost\n(`costMicros` and `costUsd`), largest cost first, with a `label` (email or name) when grouped by identity or agent;\n`totals` adds them up. A call an agent made for a person counts under the person by identity and under the agent by\nagent.\n\n```ts\nconst report = await iam.api.inference.usage(credential, { tenantId, groupBy: 'agent' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/inference#usage"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "from": {
          "type": "number"
         },
         "to": {
          "type": "number"
         },
         "identityId": {
          "type": "string"
         },
         "agentId": {
          "type": "string"
         },
         "model": {
          "type": "string"
         },
         "groupBy": {
          "type": "string",
          "enum": [
           "agent",
           "identity",
           "day",
           "model"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/UsageReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/invariants/create": {
   "post": {
    "operationId": "invariants.create",
    "tags": [
     "invariants"
    ],
    "summary": "Stores an invariant and returns it with its current result.",
    "description": "- **Permission:** `iam:invariants:manage` on the tenant.\n- **Audited as:** `iam:invariants:manage`.\n- **Errors:** `INVALID_INPUT` for a missing or overlong name (100 characters), a `subject` that is not exactly one\n  of the four shapes, an undeclared identity attribute or a value of the wrong type, or an invalid `expect`, `mode`,\n  or `assumeMfa`; `INVALID_ACTION` when the action is not in the catalog; `NOT_FOUND` when the named identity,\n  group, or resource does not exist; `RESOURCE_RESOLVER_REQUIRED` for an application-owned resource type without a\n  resolver; `CONFLICT` when an invariant with the same name (ignoring case) exists; `LIMIT_EXCEEDED` when the tenant\n  already has 100 invariants.\n\nThe resource must resolve now, because an invariant over a resource that does not exist could never be evaluated.\nThe returned `result` shows at once whether the invariant holds; creating one in `enforce` mode succeeds even when it\nis already broken.\n\n```ts\nconst { invariant, result } = await iam.api.invariants.create(credential, {\n  tenantId,\n  name: 'Contractors never approve payments',\n  subject: { attribute: { name: 'contractor', value: true } },\n  action: 'payments:approve',\n  resource: { type: 'ledger', id: 'main' },\n  expect: 'deny',\n  mode: 'enforce',\n});\n// result.passed, result.violations: [{ identity: { id, name }, reason }]\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/invariants#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/InvariantInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "invariant": {
             "$ref": "#/components/schemas/AccessInvariant"
            },
            "result": {
             "$ref": "#/components/schemas/InvariantResult"
            }
           },
           "required": [
            "invariant",
            "result"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/invariants/delete": {
   "post": {
    "operationId": "invariants.delete",
    "tags": [
     "invariants"
    ],
    "summary": "Deletes an invariant, ending its monitoring and enforcement.",
    "description": "- **Permission:** `iam:invariants:manage` on the invariant (`iam/{invariantId}`).\n- **Audited as:** `iam:invariants:manage`.\n- **Errors:** `NOT_FOUND` when the invariant is not in this tenant.\n\nAn enforced invariant blocks deleting the group or resource it names; delete or change the invariant first.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/invariants#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invariantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "invariantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/invariants/list": {
   "post": {
    "operationId": "invariants.list",
    "tags": [
     "invariants"
    ],
    "summary": "Lists the tenant's invariants by name, with the outcome of the last scheduled check.",
    "description": "- **Permission:** `iam:invariants:read` on the tenant.\n- **Audited as:** `iam:invariants:read`.\n\nEach invariant carries `lastCheck` (`at`, `passed`, the violating identity ids, and an `error` message) once\n`iam.checkInvariants` has run. For a fresh evaluation, call `run`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/invariants#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/AccessInvariant"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/invariants/run": {
   "post": {
    "operationId": "invariants.run",
    "tags": [
     "invariants"
    ],
    "summary": "Evaluates every invariant, or one, against the current configuration and reports which pass.",
    "description": "- **Permission:** `iam:invariants:read` on the tenant, or on the invariant (`iam/{invariantId}`) when you pass\n  `invariantId`.\n- **Audited as:** `iam:invariants:read`.\n- **Errors:** `NOT_FOUND` when `invariantId` is not in this tenant.\n\nThe result has `generatedAt`, a `summary` (`passed`, `failed`, and `errors` counts), and one entry per invariant\nwith `passed`, `evaluated`, `truncated`, the `violations`, and an `error` when it could not be evaluated. It changes\nnothing: it does not update `lastCheck` or record audit events for broken invariants. In CI, run\n[`check-invariants`](/docs/reference/cli#check-invariants) with `--fail-on-broken` after applying configuration.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/invariants#run"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invariantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/InvariantRunResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/invariants/update": {
   "post": {
    "operationId": "invariants.update",
    "tags": [
     "invariants"
    ],
    "summary": "Changes any field of an invariant and returns it with its new result.",
    "description": "- **Permission:** `iam:invariants:manage` on the invariant (`iam/{invariantId}`).\n- **Audited as:** `iam:invariants:manage`.\n- **Errors:** `NOT_FOUND` when the invariant is not in this tenant; otherwise the same as `create`.\n\nFields you leave out keep their values; `description: ''` removes the description. Use it to switch between\n`monitor` and `enforce`, or to point an invariant at a new group or resource before deleting the old one.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/invariants#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "name": {
            "type": "string"
           },
           "description": {
            "type": "string"
           },
           "subject": {
            "anyOf": [
             {
              "type": "object",
              "properties": {
               "identityId": {
                "type": "string"
               }
              },
              "required": [
               "identityId"
              ],
              "additionalProperties": false
             },
             {
              "type": "object",
              "properties": {
               "groupId": {
                "type": "string"
               }
              },
              "required": [
               "groupId"
              ],
              "additionalProperties": false
             },
             {
              "type": "object",
              "properties": {
               "attribute": {
                "type": "object",
                "properties": {
                 "name": {
                  "type": "string"
                 },
                 "value": {
                  "anyOf": [
                   {
                    "type": "string"
                   },
                   {
                    "type": "number"
                   },
                   {
                    "type": "boolean",
                    "const": false
                   },
                   {
                    "type": "boolean",
                    "const": true
                   }
                  ]
                 }
                },
                "required": [
                 "name",
                 "value"
                ],
                "additionalProperties": false
               }
              },
              "required": [
               "attribute"
              ],
              "additionalProperties": false
             },
             {
              "type": "object",
              "properties": {
               "everyone": {
                "type": "boolean",
                "const": true
               }
              },
              "required": [
               "everyone"
              ],
              "additionalProperties": false
             }
            ]
           },
           "action": {
            "type": "string"
           },
           "resource": {
            "type": "object",
            "properties": {
             "type": {
              "type": "string"
             },
             "id": {
              "type": "string"
             }
            },
            "required": [
             "type",
             "id"
            ],
            "additionalProperties": false
           },
           "expect": {
            "type": "string",
            "enum": [
             "allow",
             "deny"
            ]
           },
           "mode": {
            "type": "string",
            "enum": [
             "enforce",
             "monitor"
            ]
           },
           "assumeMfa": {
            "type": "boolean"
           }
          },
          "additionalProperties": false
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "invariantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "invariantId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "invariant": {
             "$ref": "#/components/schemas/AccessInvariant"
            },
            "result": {
             "$ref": "#/components/schemas/InvariantResult"
            }
           },
           "required": [
            "invariant",
            "result"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/links/create": {
   "post": {
    "operationId": "links.create",
    "tags": [
     "links"
    ],
    "summary": "Links the caller's account to another account of theirs in a different tenant, proving control of both.",
    "description": "- **Permission:** The caller's own session, plus a credential for the target account. Both must be recently\n  authenticated user sessions.\n- **Audited as:** `identity:link`, in the caller's tenant.\n- **Errors:** `LINKING_DISABLED` when the deployment does not enable linked onboarding; `RECENT_AUTH_REQUIRED` when\n  either session is not recently authenticated or is a temporary credential; `IMPERSONATION_RESTRICTED` from a\n  \"view as\" session; `INVALID_LINK` when either account is a root administrator, either credential is not a user\n  session, or both accounts are in the same tenant; `CONFLICT` when the accounts are already linked.\n\nRecent authentication (by default within the last five minutes) on both sides is the proof: the person has just\nsigned in to each account. Re-linking two accounts whose earlier link was revoked restores that link.\n\n```ts\n// The person has just signed in to their other account, for example in a second sign-in form.\nconst link = await iam.api.links.create(credential, {\n  targetCredential: { token: otherAccountToken },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/links#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "targetCredential": {
          "$ref": "#/components/schemas/CredentialInput"
         }
        },
        "required": [
         "targetCredential"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/IdentityLink"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/links/list": {
   "post": {
    "operationId": "links.list",
    "tags": [
     "links"
    ],
    "summary": "Lists the accounts linked to the caller's account, for an account-switcher menu.",
    "description": "- **Permission:** The caller's own session.\n\nEach entry has the link `id` and the other account's identity (`identityId`, `name`, `email`, `status`) and tenant\n(`tenantId`, `tenantName`, `tenantSlug`, `tenantStatus`). Show the statuses so people understand why a disabled\naccount or a suspended organization cannot be opened. Revoked links are left out. The call is not audited.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/links#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/LinkedAccount"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/links/revoke": {
   "post": {
    "operationId": "links.revoke",
    "tags": [
     "links"
    ],
    "summary": "Removes a link the person no longer wants.",
    "description": "- **Permission:** The caller's own session, recently authenticated, as either side of the link.\n- **Audited as:** `identity:unlink`.\n- **Errors:** `NOT_FOUND` when the link does not exist or the caller is not one of its two accounts;\n  `RECENT_AUTH_REQUIRED` without recent authentication.\n\nEither account may revoke the link. The accounts themselves are unaffected, and switching between them stops\nworking at once.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/links#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "linkId": {
          "type": "string"
         }
        },
        "required": [
         "linkId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "boolean"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/links/switch": {
   "post": {
    "operationId": "links.switch",
    "tags": [
     "links"
    ],
    "summary": "Opens a fresh session for a linked account, using a recently authenticated credential for that account.",
    "description": "- **Permission:** The caller's own user session, plus a recently authenticated user-session credential for the\n  target account; both accounts must be the two sides of the link.\n- **Audited as:** `identity:switch`, in the target account's tenant.\n- **Errors:** `RECENT_AUTH_REQUIRED` when the target credential is not recently authenticated; `INVALID_LINK` when\n  the link is missing or revoked, does not join these two accounts, or either side is a root administrator or not\n  a user session.\n\nThe new session carries the target credential's MFA state and authentication time, so the target tenant's rules\nstill apply. Over HTTP the response sets the browser's session cookie, which moves the browser to the target\naccount; the caller's original session is not ended.\n\n```ts\nconst { token, session } = await iam.api.links.switch(credential, {\n  linkId,\n  targetCredential: { token: freshTargetToken },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/links#switch"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "linkId": {
          "type": "string"
         },
         "targetCredential": {
          "$ref": "#/components/schemas/CredentialInput"
         }
        },
        "required": [
         "linkId",
         "targetCredential"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SessionResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/create": {
   "post": {
    "operationId": "oidcProviders.create",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Registers an OIDC provider whose tokens the tenant's web-identity trusts can admit.",
    "description": "- **Permission:** `iam:oidc-providers:create` on the tenant, with recent authentication and an active grant\n  authority.\n- **Audited as:** `iam:oidc-providers:create`.\n- **Errors:** `FEATURE_DISABLED` when web identity is not enabled; `CONFLICT` when the tenant already has a provider\n  for this issuer; `INVALID_INPUT` for an issuer that is not https, is IAM's own, or is not on\n  `sts.webIdentity.allowedIssuers`, a `jwksUri` that is not https on port 443 or points at a private address, private\n  or weak static keys, both `jwks` and `jwksUri`, or an algorithm outside RS256, RS384, RS512, PS256, PS384, PS512,\n  ES256, ES384, and EdDSA; `RECENT_AUTH_REQUIRED`; `GRANT_AUTHORITY_REQUIRED`.\n\nDefaults: algorithms `RS256` and `ES256`, tokens at most 3600 seconds old and long (`maxTokenLifetimeSeconds`), 30\nseconds of clock tolerance, single-use replay protection, and enabled.\n\n```ts\nconst github = await iam.api.oidcProviders.create(credential, {\n  tenantId,\n  name: 'GitHub Actions',\n  issuer: 'https://token.actions.githubusercontent.com',\n  audiences: ['https://iam.example.com'],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OidcProviderCreateInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicOidcProvider"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/delete": {
   "post": {
    "operationId": "oidcProviders.delete",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Deletes a provider that no live trust uses any more.",
    "description": "- **Permission:** `iam:oidc-providers:delete` on the provider, with recent authentication, and the grant authority it\n  was created under (or root).\n- **Audited as:** `iam:oidc-providers:delete`.\n- **Errors:** `CONFLICT` (409) while an unrevoked trust still names the provider; `ACCESS_DENIED` when another\n  administrator's authority created it; `NOT_FOUND`; `RECENT_AUTH_REQUIRED`.\n\nRevoke the trusts that use it first; revoking a trust already ends its sessions.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "providerId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "providerId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/get": {
   "post": {
    "operationId": "oidcProviders.get",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Returns one provider.",
    "description": "- **Permission:** `iam:oidc-providers:read` on the provider.\n- **Audited as:** `iam:oidc-providers:read`.\n- **Errors:** `NOT_FOUND` when the provider is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "providerId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "providerId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicOidcProvider"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/list": {
   "post": {
    "operationId": "oidcProviders.list",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Lists the tenant's OIDC providers, oldest first.",
    "description": "- **Permission:** `iam:oidc-providers:read` on the tenant.\n- **Audited as:** `iam:oidc-providers:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicOidcProvider"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/revokeSessions": {
   "post": {
    "operationId": "oidcProviders.revokeSessions",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Ends the web-identity sessions issued through a provider before a point in time, across every trust that uses it.",
    "description": "- **Permission:** `iam:roles:revoke-sessions` on the provider, with recent authentication.\n- **Audited as:** `iam:roles:revoke-sessions` and `role:sessions-revoked` (with the watermark and the number of\n  sessions deleted).\n- **Errors:** `INVALID_INPUT` when `before` is not a whole number of milliseconds, is negative, or lies in the future;\n  `NOT_FOUND`; `RECENT_AUTH_REQUIRED`.\n\n`before` defaults to now, which ends every session issued so far. The provider's `sessionsRevokedBefore` watermark\nonly moves forward, so a session issued earlier is refused at its next use even if it is created concurrently, and\nthe matching rows are deleted at once. Use it to end sessions without changing the provider, for example after a\nworkload's token leaked. After a provider's signing key leaks, removing the key with [`update`](#update) is enough:\nthe update itself ends every session issued so far, so a separate revoke is not needed. Session JWTs checked offline\nby other services stay valid until they expire.\n\n```ts\nconst { revoked } = await iam.api.oidcProviders.revokeSessions(credential, { tenantId, providerId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#revokesessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OidcProviderRevokeSessionsInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "providerId": {
             "type": "string"
            },
            "sessionsRevokedBefore": {
             "type": "number"
            },
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "providerId",
            "sessionsRevokedBefore",
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/oidcProviders/update": {
   "post": {
    "operationId": "oidcProviders.update",
    "tags": [
     "oidcProviders"
    ],
    "summary": "Changes a provider's name, audiences, keys, algorithms, token limits, replay protection, or enabled state.",
    "description": "- **Permission:** `iam:oidc-providers:update` on the provider, with recent authentication, and the grant authority it\n  was created under (or root).\n- **Audited as:** `iam:oidc-providers:update`.\n- **Errors:** `FEATURE_DISABLED` when web identity is off, unless the update only sets `enabled: false`;\n  `INVALID_INPUT` when the update changes nothing, tries to change the issuer, or gives invalid values (as for\n  `create`); `ACCESS_DENIED` when another administrator's authority created it; `NOT_FOUND`; `RECENT_AUTH_REQUIRED`.\n\nOnly the fields you pass change; `jwksUri: null` or `jwks: null` removes that key source. Cached keys are dropped, so\nthe next token is checked against the new settings.\n\nSome changes end every session issued through the provider so far, across all of its trusts, because those sessions\nwere admitted under the old rules: changing `jwks` or `jwksUri` (adding a key for a rotation counts), `algorithms`,\n`audiences`, `maxTokenLifetimeSeconds` or `clockToleranceSeconds`, and disabling the provider. Such an update moves\nthe provider's `sessionsRevokedBefore` watermark to now (the response carries it) and deletes the matching session\nrows, as [`revokeSessions`](#revokesessions) would; workloads simply exchange a fresh token. Changing `name` or\n`replayProtection`, or enabling the provider, keeps live sessions. Plan key rotations for a quiet moment, or publish\nkeys through `jwksUri` or discovery so rotations need no update at all.\n\n`enabled: false` is the kill switch: a disabled provider admits no exchanges, and the sessions issued through it end\nfor good, so enabling it again does not bring them back.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/oidc-providers#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OidcProviderUpdateInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicOidcProvider"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/createFlow": {
   "post": {
    "operationId": "onboarding.createFlow",
    "tags": [
     "onboarding"
    ],
    "summary": "Creates a flow at this tenant's level.",
    "description": "- **Permission:** `iam:onboarding:manage` on the tenant. Form fields that fill identity attributes (`attribute`) also\n  need `iam:identities:update`; `completionGroupIds` need `iam:groups:update` on each group and the use of the grant\n  authorities behind the group's bindings. Both are refused from role sessions, session tokens, and impersonation.\n- **Audited as:** `iam:onboarding:manage`.\n- **Errors:** `CONFLICT` (409) for a name the tenant already uses; `LIMIT_EXCEEDED` (409) past 50 flows;\n  `INVALID_INPUT` for an unknown field, a step kind the audience does not allow, duplicate step IDs, an undeclared or\n  mistyped attribute, a textarea field mapped to an attribute, attribute mappings on a flow that reaches only tenants\n  below, `tenantTypes` that cannot exist below the tenant, `descendants` on a tenant type without\n  children, completion groups on a flow that reaches only descendants, or a rule that tests `identity.groups` on such a\n  flow; `NOT_FOUND` for an unknown completion group; `ACCESS_DENIED` when the attribute or group permissions are\n  missing.\n\nSteps (1-25) have a stable `id` (lowercase letters, digits, dashes) and a `kind`: `form` (1-20 `fields`), `acknowledge`\n(`content`), `task` (`url`, `verification: 'self' | 'admin'`), and for member flows `agreement` (by name),\n`verify-email`, `mfa`, `passkey`; for tenant flows `check` (`verified-domain`, `members`, `owners`, `mfa-policy`,\n`agreement`, `slug`, `sso`, `directory-sync`, `member-onboarding`, with `minimum` for members and owners). `appliesTo`\ndefaults to `descendants` at the root and `tenant` elsewhere.\n\n```ts\nawait iam.api.onboarding.createFlow(rootCredential, {\n  tenantId: rootTenantId,\n  name: 'Platform essentials',\n  audience: 'member',\n  locked: true,\n  steps: [\n    { id: 'conduct', kind: 'acknowledge', title: 'Acceptable use', content: 'Use the service lawfully.' },\n    { id: 'mfa', kind: 'mfa', title: 'Set up two-step verification' },\n  ],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#createflow"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/OnboardingFlowInput"
         },
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId"
          ],
          "additionalProperties": false
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/OnboardingFlow"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/deleteFlow": {
   "post": {
    "operationId": "onboarding.deleteFlow",
    "tags": [
     "onboarding"
    ],
    "summary": "Deletes a flow with every progress record of it, in every tenant it reached, and removes it from tenants' disabledFlowIds.",
    "description": "- **Permission:** `iam:onboarding:manage` on the flow.\n- **Audited as:** `iam:onboarding:manage`.\n- **Errors:** `NOT_FOUND` when this tenant does not define the flow.\n\nReturns `{ deleted: true, progressRemoved }`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#deleteflow"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "flowId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            },
            "progressRemoved": {
             "type": "number"
            }
           },
           "required": [
            "deleted",
            "progressRemoved"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/effective": {
   "post": {
    "operationId": "onboarding.effective",
    "tags": [
     "onboarding"
    ],
    "summary": "Everything onboarding looks like from this tenant, for administration pages.",
    "description": "- **Permission:** `iam:onboarding:read` on the tenant.\n- **Audited as:** `iam:onboarding:read`.\n\nReturns the `levels` (root first), `memberFlows` reaching the tenant's people (own and inherited, each with `source`,\n`inherited`, `disabledBy`, and `canDisable`), the `setupFlows` the tenant's administrators complete, its `ownFlows`,\nthe `descendantTypes` a flow may target, the declared `identityAttributes`, and `settings` (`own` and `resolved`, with\nthe level each value came from). Inherited flows omit the defining tenant's completion groups and author.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#effective"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/EffectiveOnboarding"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/getFlow": {
   "post": {
    "operationId": "onboarding.getFlow",
    "tags": [
     "onboarding"
    ],
    "summary": "Returns one flow this tenant defines.",
    "description": "- **Permission:** `iam:onboarding:read` on the flow.\n- **Audited as:** `iam:onboarding:read`.\n- **Errors:** `NOT_FOUND` when this tenant does not define the flow.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#getflow"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "flowId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/OnboardingFlow"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/listFlows": {
   "post": {
    "operationId": "onboarding.listFlows",
    "tags": [
     "onboarding"
    ],
    "summary": "Lists the flows this tenant defines, oldest first, optionally for one audience.",
    "description": "- **Permission:** `iam:onboarding:read` on the tenant.\n- **Audited as:** `iam:onboarding:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#listflows"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "audience": {
          "type": "string",
          "enum": [
           "tenant",
           "member"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/OnboardingFlow"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/memberProgress": {
   "post": {
    "operationId": "onboarding.memberProgress",
    "tags": [
     "onboarding"
    ],
    "summary": "One person's member flows in this tenant, with the state of every step and their answers, and pending (required flows still open).",
    "description": "- **Permission:** `iam:onboarding:read` on the identity.\n- **Audited as:** `iam:onboarding:read`.\n- **Errors:** `NOT_FOUND` for an identity outside the tenant or deleted.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#memberprogress"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "identity": {
             "type": "object",
             "properties": {
              "id": {
               "type": "string"
              },
              "name": {
               "type": "string"
              },
              "email": {
               "type": "string"
              }
             },
             "required": [
              "id",
              "name"
             ],
             "additionalProperties": false
            },
            "flows": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/OnboardingFlowStatus"
             }
            },
            "pending": {
             "type": "number"
            }
           },
           "required": [
            "identity",
            "flows",
            "pending"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/mine": {
   "post": {
    "operationId": "onboarding.mine",
    "tags": [
     "onboarding"
    ],
    "summary": "The caller's own onboarding: the welcome screen and their member flows, with step states and their own answers.",
    "description": "- **Permission:** None beyond an ordinary session (or API key) of the tenant.\n- **Audited as:** `onboarding:complete` for each flow seen complete for the first time in its current version.\n- **Errors:** `ACCESS_DENIED` from a role session, a session token, or another tenant's session.\n\nReturns `{ tenant, welcome, flows, pending, complete }`. Each flow carries `source` (the level that defines it),\n`required`, `steps` (`state`: `pending`, `complete`, `submitted`, `rejected` with the reviewer's `note`, or\n`unavailable`), `done`, and `total`. Completion groups of newly finished flows are applied after the read, in their own\ntransaction; a refusal (such as a separation-of-duties rule) is kept as the progress record's `completionError` and\nretried on the next read. Impersonating administrators see the checklist, but nothing is recorded. Service accounts\nhave no flows.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#mine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MyOnboarding"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/progress": {
   "post": {
    "operationId": "onboarding.progress",
    "tags": [
     "onboarding"
    ],
    "summary": "Progress through one flow as this tenant sees it.",
    "description": "- **Permission:** `iam:onboarding:read` on the flow.\n- **Audited as:** `iam:onboarding:read`.\n- **Errors:** `NOT_FOUND` when the flow neither belongs to nor reaches this tenant (setup flows report only at the\n  tenant that defines them).\n\nFor member flows, `members` lists this tenant's people the flow applies to, with `done`, `total`, `complete`,\n`awaiting` (tasks waiting for review), and `answers` by step. A flow defined here that reaches tenants below adds\n`descendants`: per tenant, how many people it applies to and how many finished, never names. For setup flows,\n`tenants` lists every descendant tenant the flow applies to, with its answers. `summary` counts subjects and completions;\n`truncated` is set past 1000 descendant tenants.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#progress"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "flowId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/OnboardingProgressReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/resetProgress": {
   "post": {
    "operationId": "onboarding.resetProgress",
    "tags": [
     "onboarding"
    ],
    "summary": "Clears progress through a flow, or one step of it (stepId), so people or tenants go through it again.",
    "description": "- **Permission:** `iam:onboarding:manage` on the flow. The tenant that defines the flow may reset anyone it reaches\n  (everyone when `subjectId` is omitted); a tenant a member flow reaches may reset its own people.\n- **Audited as:** `onboarding:reset`, with `reset` (the number of progress records) and the `subjectId` / `stepId`.\n- **Errors:** `NOT_FOUND` for a flow that does not reach the tenant or an unknown step.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#resetprogress"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         },
         "subjectId": {
          "type": "string"
         },
         "stepId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "flowId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "reset": {
             "type": "number"
            }
           },
           "required": [
            "reset"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/setSettings": {
   "post": {
    "operationId": "onboarding.setSettings",
    "tags": [
     "onboarding"
    ],
    "summary": "Replaces this tenant's onboarding settings: welcomeTitle, welcomeMessage, supportEmail, supportUrl (empty or null falls back to the level above), and disabledFlowIds, the inherited member flows switc…",
    "description": "- **Permission:** `iam:onboarding:manage` on the tenant.\n- **Audited as:** `iam:onboarding:manage`.\n- **Errors:** `INVALID_INPUT` for a flow that is not an inherited member flow of the tenant, a locked flow, a malformed\n  email or URL, or an unknown field.\n\nA switch kept from before whose flow no longer reaches the tenant (deleted, retargeted, or locked since) is dropped\nquietly, so re-saving never fails on someone else's change.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#setsettings"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OnboardingSettingsInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/OnboardingSettings"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/setup": {
   "post": {
    "operationId": "onboarding.setup",
    "tags": [
     "onboarding"
    ],
    "summary": "This tenant's own setup checklists (the tenant flows defined above it), with the state of every step and the answers.",
    "description": "- **Permission:** `iam:onboarding:read` on the tenant.\n- **Audited as:** `iam:onboarding:read`, and `onboarding:complete` for a checklist seen complete for the first time.\n\nReturns the same shape as `mine`. Check steps carry a `detail` such as `1 of 2 owners`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#setup"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MyOnboarding"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/submitSetupStep": {
   "post": {
    "operationId": "onboarding.submitSetupStep",
    "tags": [
     "onboarding"
    ],
    "summary": "Completes a form, acknowledge, or task step of this tenant's setup.",
    "description": "- **Permission:** `iam:onboarding:manage` on the flow.\n- **Audited as:** `onboarding:step`, and `onboarding:complete` when the checklist is done.\n- **Errors:** `INVALID_INPUT` for a check step (they follow the tenant's state), missing or invalid answers, or an\n  acknowledgement without `acknowledged: true`; `IMPERSONATION_RESTRICTED` while impersonating; `NOT_FOUND` for a flow\n  that does not apply to the tenant or an unknown step.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#submitsetupstep"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         },
         "stepId": {
          "type": "string"
         },
         "answers": {
          "type": "object",
          "properties": {},
          "additionalProperties": {}
         },
         "acknowledged": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "flowId",
         "stepId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "flow": {
             "$ref": "#/components/schemas/OnboardingFlowStatus"
            }
           },
           "required": [
            "flow"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/submitStep": {
   "post": {
    "operationId": "onboarding.submitStep",
    "tags": [
     "onboarding"
    ],
    "summary": "Completes one step of the caller's own member flow: answers for a form, acknowledged: true for an acknowledgement, nothing for a task (an admin-verified task is submitted for review).",
    "description": "- **Permission:** None beyond an ordinary session of the tenant.\n- **Audited as:** `onboarding:step` (with the attributes the answers filled), and `onboarding:complete` when the flow is\n  done.\n- **Errors:** `INVALID_INPUT` for a step that completes on its own, a missing required answer, an answer of the wrong\n  type or outside a select's choices, or an unknown answer key; `IMPERSONATION_RESTRICTED` while impersonating;\n  `NOT_FOUND` for a flow that does not apply to the caller or an unknown step.\n\nAnswers mapped to identity attributes fill only empty attributes, and only when the caller's own tenant defines the\nflow (an inherited flow records answers but never writes the tenant's identities); values an administrator or\ndirectory sync set are kept. Returns `{ flow, attributesFilled }`.\n\n```ts\nawait iam.api.onboarding.submitStep(credential, {\n  tenantId,\n  flowId,\n  stepId: 'profile',\n  answers: { department: 'Engineering' },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#submitstep"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         },
         "stepId": {
          "type": "string"
         },
         "answers": {
          "type": "object",
          "properties": {},
          "additionalProperties": {}
         },
         "acknowledged": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "flowId",
         "stepId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "flow": {
             "$ref": "#/components/schemas/OnboardingFlowStatus"
            },
            "attributesFilled": {
             "type": "array",
             "items": {
              "type": "string"
             }
            }
           },
           "required": [
            "flow",
            "attributesFilled"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/updateFlow": {
   "post": {
    "operationId": "onboarding.updateFlow",
    "tags": [
     "onboarding"
    ],
    "summary": "Changes any field of a flow but its audience.",
    "description": "- **Permission:** `iam:onboarding:manage` on the flow, plus the attribute and group permissions of `createFlow` for newly\n  mapped attributes and newly added completion groups.\n- **Audited as:** `iam:onboarding:manage`.\n- **Errors:** as `createFlow`, and `NOT_FOUND` when this tenant does not define the flow.\n\n`null` clears `description`, `tenantTypes`, `rule`, and `completionGroupIds`. Pausing (`enabled: false`) asks nobody;\nthe flow's `effectiveFrom` is set the first time it is enabled.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#updateflow"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OnboardingFlowUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/OnboardingFlow"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/onboarding/verifyStep": {
   "post": {
    "operationId": "onboarding.verifyStep",
    "tags": [
     "onboarding"
    ],
    "summary": "Approves (the default) or sends back (approve: false, with an optional note) an administrator-verified task.",
    "description": "- **Permission:** `iam:onboarding:manage`. For member flows the caller administers the person's tenant (`tenantId`)\n  and `subjectId` is the person; for setup flows the caller administers the tenant that defines the flow and\n  `subjectId` is the tenant being set up.\n- **Audited as:** `onboarding:verify` or `onboarding:reject`.\n- **Errors:** `INVALID_INPUT` for a step that is not an administrator-verified task, or when people verify their own\n  onboarding; `IMPERSONATION_RESTRICTED` while impersonating; `NOT_FOUND` when the flow does not apply to the subject.\n\nApproving the last open step records the flow as complete; completion groups are applied the next time the person\nreads their onboarding.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/onboarding#verifystep"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "flowId": {
          "type": "string"
         },
         "subjectId": {
          "type": "string"
         },
         "stepId": {
          "type": "string"
         },
         "approve": {
          "type": "boolean"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "flowId",
         "subjectId",
         "stepId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "flow": {
             "$ref": "#/components/schemas/OnboardingFlowStatus"
            }
           },
           "required": [
            "flow"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/approveRequest": {
   "post": {
    "operationId": "packages.approveRequest",
    "tags": [
     "packages"
    ],
    "summary": "Grants a pending package request by assigning the package to the requester under your own authority.",
    "description": "- **Permission:** `iam:packages:approve` on the package; when the package names approvers, membership of the\n  approver group or being the requester's manager (or root); plus the rights `assign` needs for every role and group.\n- **Audited as:** `iam:packages:approve`, plus `package:request-approved` with the assignment's counts, skips, and end.\n- **Errors:** `INVALID_TRANSITION` when the request is no longer pending or the package is no longer requestable;\n  `NOT_FOUND` when the request is not in this tenant; `INVALID_INPUT` for your own request or an end the package\n  does not allow; `ACCESS_DENIED`\n  when you are not a designated approver or cannot grant one of the packaged roles or groups;\n  `IMPERSONATION_RESTRICTED` from an impersonation session; `GRANT_AUTHORITY_REQUIRED` without a grant authority;\n  `SOD_CONFLICT` when the assignment would create a [separation-of-duties](/docs/guides/authorization/separation-of-duties)\n  conflict; `INVARIANT_VIOLATION` when it would break an enforced invariant.\n\nThe assignment ends at the `expiresAt` you pass or, without it, at the end the requester asked for, validated\nagainst the package's `maxDurationMs`. The optional `note` is stored on the request. When the deployment sends\nemail, the requester receives a `package-decided` message.\n\n```ts\nawait iam.api.packages.approveRequest(credential, {\n  tenantId,\n  requestId,\n  expiresAt: Date.now() + 14 * 24 * 60 * 60 * 1000, // shorter than the 30 days they asked for\n  note: 'Two weeks covers the migration.',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#approverequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageRequestSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/assign": {
   "post": {
    "operationId": "packages.assign",
    "tags": [
     "packages"
    ],
    "summary": "Grants a package to a person: one binding per role and one membership per group, all ending together.",
    "description": "- **Permission:** `iam:packages:assign` on the package, `iam:bindings:create` on each packaged role,\n  `iam:groups:update` on each packaged group, and authority over the role bindings of each group the person joins.\n- **Audited as:** `iam:packages:assign`, plus `package:assign` with the created counts, `skipped`, end, and\n  justification.\n- **Errors:** `CONFLICT` when the person already holds a manual assignment of the package that is not broken;\n  `NOT_FOUND`\n  when the package or person is not in this tenant or the person is deleted; `INVALID_INPUT` when an end is\n  required and missing, exceeds `maxDurationMs`, or a required justification is missing; `ACCESS_DENIED` without\n  the rights for one of the parts; `GRANT_AUTHORITY_REQUIRED` without a grant authority; `SOD_CONFLICT` and\n  `INVARIANT_VIOLATION` when the grant would create a conflict or break an enforced invariant.\n\nEverything happens in one transaction, so either the whole package is granted or nothing is. The result carries the\nassignment, `created` (bindings and memberships), `skipped` (memberships the person already held for as long), and\n`replacedAutomatic`. Assigning a package the person holds through its rule takes the automatic assignment over: it\nbecomes a manual one under your authority. Assigning also marks the person's pending request for the package as\napproved.\n\n```ts\nconst result = await iam.api.packages.assign(credential, {\n  tenantId,\n  packageId,\n  identityId,\n  expiresAt: Date.now() + 90 * 24 * 60 * 60 * 1000,\n  justification: 'Joins the Atlas project for Q4',\n});\n// result.created: { bindings: 2, memberships: 1 }, result.skipped: [...]\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#assign"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AssignmentInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "created": {
             "type": "object",
             "properties": {
              "bindings": {
               "type": "number"
              },
              "memberships": {
               "type": "number"
              }
             },
             "required": [
              "bindings",
              "memberships"
             ],
             "additionalProperties": false
            },
            "skipped": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "replacedAutomatic": {
             "type": "boolean"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "packageId": {
             "type": "string"
            },
            "identityId": {
             "type": "string"
            },
            "assignedBy": {
             "type": "string"
            },
            "assignedAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "justification": {
             "type": "string"
            },
            "bindingIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "membershipIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "packageName": {
             "type": "string"
            },
            "identityName": {
             "type": "string"
            },
            "identityEmail": {
             "type": "string"
            },
            "expired": {
             "type": "boolean"
            },
            "broken": {
             "type": "boolean"
            },
            "automatic": {
             "type": "boolean"
            }
           },
           "required": [
            "created",
            "skipped",
            "replacedAutomatic",
            "id",
            "tenantId",
            "packageId",
            "identityId",
            "assignedBy",
            "assignedAt",
            "bindingIds",
            "membershipIds",
            "packageName",
            "identityName",
            "expired",
            "broken",
            "automatic"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/cancelRequest": {
   "post": {
    "operationId": "packages.cancelRequest",
    "tags": [
     "packages"
    ],
    "summary": "Withdraws one of your own pending package requests.",
    "description": "- **Permission:** `iam:packages:request` on the package, and you must be the requester.\n- **Audited as:** `iam:packages:request`, plus `package:request-cancelled`.\n- **Errors:** `ACCESS_DENIED` when the request is someone else's; `INVALID_TRANSITION` when it is no longer pending;\n  `NOT_FOUND` when the request is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#cancelrequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageRequestSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/create": {
   "post": {
    "operationId": "packages.create",
    "tags": [
     "packages"
    ],
    "summary": "Defines a package of roles and groups, optionally requestable or assigned automatically by a rule.",
    "description": "- **Permission:** `iam:packages:create` on the tenant. With `autoAssign`, also `iam:packages:assign` on the package\n  and every right `assign` needs, including authority over the packaged groups' bindings.\n- **Audited as:** `iam:packages:create`, plus `package:auto-rule` when a rule is set.\n- **Errors:** `CONFLICT` when a package with the same name (ignoring case) exists; `INVALID_INPUT` when it has no\n  role or group, more than 50 of either, a `maxDurationMs` outside one minute to ten years, or a rule that does not\n  validate; `PROTECTED_RESOURCE` for a protected (owner) role; `NOT_FOUND` when a role, group, or approver group is\n  not in this tenant; `INVALID_POLICY` when a rule clause is not a valid condition block. With a rule, also\n  `IMPERSONATION_RESTRICTED`, `ACCESS_DENIED`, and `GRANT_AUTHORITY_REQUIRED` from the owner checks.\n\nA new package without a rule grants nothing until you assign it or someone requests it. With `autoAssign` you\nbecome the rule's owner, and the rule must be set from an ordinary session or API key, never an assumed role. After\nthe save commits, a first reconcile of up to 200 changes runs and its result is returned as `reconcile`; if it\nfails, the call still succeeds and the scheduled job catches up. The save also pre-approves the rule's planned counts\nfor a day, so scheduled runs in that window apply the rest without the safety brake holding them back.\n\n```ts\n// Everyone in engineering (people, not service accounts) gets the kit automatically.\nconst pkg = await iam.api.packages.create(credential, {\n  tenantId,\n  name: 'Engineering onboarding',\n  roleIds: [developerRole.id],\n  groupIds: [engineeringGroup.id],\n  autoAssign: {\n    include: [{ StringEquals: { 'principal.kind': 'user', 'principal.department': 'engineering' } }],\n    graceMs: 7 * 24 * 60 * 60 * 1000,\n  },\n});\n// pkg.reconcile?.assigned: how many people received it right away\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PackageInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/PublicPackage"
            },
            {
             "type": "object",
             "properties": {
              "reconcile": {
               "$ref": "#/components/schemas/PackageReconcileResult"
              }
             },
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/delete": {
   "post": {
    "operationId": "packages.delete",
    "tags": [
     "packages"
    ],
    "summary": "Deletes a package nobody holds, together with its request history.",
    "description": "- **Permission:** `iam:packages:delete` on the package.\n- **Audited as:** `iam:packages:delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) while any live assignment exists; `NOT_FOUND` when the package is not in this\n  tenant.\n\nRevoke manual assignments first. For a rule package, set `autoAssign` to `null` with `update` so reconciliation\nremoves the automatic assignments, then delete. Ended assignments, requests, and rule issues are deleted with the\npackage.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "packageId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/denyRequest": {
   "post": {
    "operationId": "packages.denyRequest",
    "tags": [
     "packages"
    ],
    "summary": "Refuses a pending package request, with an optional note to the requester.",
    "description": "- **Permission:** `iam:packages:approve` on the package and, when the package names approvers, membership of the\n  approver group or being the requester's manager (or root).\n- **Audited as:** `iam:packages:approve`, plus `package:request-denied`.\n- **Errors:** `INVALID_TRANSITION` when the request is no longer pending; `NOT_FOUND` when the request is not in\n  this tenant; `INVALID_INPUT` for your own request; `ACCESS_DENIED` when you are not a designated approver;\n  `IMPERSONATION_RESTRICTED` from an impersonation session.\n\nDenying needs no grant rights, since nothing is granted. When the deployment sends email, the requester receives a\n`package-decided` message with your note.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#denyrequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageRequestSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/extend": {
   "post": {
    "operationId": "packages.extend",
    "tags": [
     "packages"
    ],
    "summary": "Moves the end of a person's manual assignment, for the assignment and every record it created at once.",
    "description": "- **Permission:** `iam:packages:assign` on the package. Lengthening, or `expiresAt: null`, is granting: it also\n  needs the rights `assign` needs and a grant authority.\n- **Audited as:** `iam:packages:assign`, plus `package:extend` with the previous and new end.\n- **Errors:** `NOT_FOUND` when the person holds no live assignment of the package; `INVALID_TRANSITION` for an\n  automatic assignment; `INVALID_INPUT` when `expiresAt` is missing or the new end breaks the package's\n  `maxDurationMs` (including `null` on a capped package); `ACCESS_DENIED` or `GRANT_AUTHORITY_REQUIRED` when\n  lengthening without the rights.\n\nShortening needs no more than revoking does. When you lengthen, the assignment's bindings move to your authority, so\nthe longer grant is bounded by what you may give. Shortening a shared membership hands it to another of the person's\npackages that still needs it longer. Automatic assignments end when the person stops matching the rule; assign the\npackage manually to take one over and set an end.\n\n```ts\nawait iam.api.packages.extend(credential, { tenantId, packageId, identityId, expiresAt: Date.parse('2026-12-31') });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#extend"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "expiresAt": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "packageId",
         "identityId",
         "expiresAt"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageAssignmentSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/get": {
   "post": {
    "operationId": "packages.get",
    "tags": [
     "packages"
    ],
    "summary": "Returns one package with its live holder counts and, for a rule package, the rule's state.",
    "description": "- **Permission:** `iam:packages:read` on the package.\n- **Audited as:** `iam:packages:read`.\n- **Errors:** `NOT_FOUND` when the package is not in this tenant.\n\n`assignments` counts live holders and `automaticAssignments` those the rule assigned. A rule package also returns\n`autoAssign` with the owner's name, `status` (`active` or `suspended`, with the reason), advice in `warnings`, and\nthe 20 newest problems in `issues`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "packageId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicPackage"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/list": {
   "post": {
    "operationId": "packages.list",
    "tags": [
     "packages"
    ],
    "summary": "Lists the tenant's packages by name, each with its live holder counts.",
    "description": "- **Permission:** `iam:packages:read` on the tenant.\n- **Audited as:** `iam:packages:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicPackage"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/listApprovals": {
   "post": {
    "operationId": "packages.listApprovals",
    "tags": [
     "packages"
    ],
    "summary": "Lists the pending package requests you may decide on.",
    "description": "- **Permission:** `iam:packages:approve` on the tenant.\n- **Audited as:** `iam:packages:approve`.\n\nUse it for an approver's inbox. Each request is included only when its package is still requestable, you hold\n`iam:packages:approve` on that package, you satisfy the package's approver rules, and the request is not your own.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#listapprovals"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PackageRequestSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/listAssignments": {
   "post": {
    "operationId": "packages.listAssignments",
    "tags": [
     "packages"
    ],
    "summary": "Lists the holders of a package, or the packages of a person, newest first.",
    "description": "- **Permission:** `iam:packages:read` on the package when you pass `packageId`, otherwise on the tenant.\n- **Audited as:** `iam:packages:read`.\n- **Errors:** `NOT_FOUND` when `packageId` is not in this tenant; `INVALID_INPUT` when `source` is not `automatic`\n  or `manual`.\n\nEnded assignments are included only with `includeExpired: true`. Each entry names the package and person and\nreports `expired`, `broken` (its bindings no longer grant), and `automatic` (assigned by the rule).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#listassignments"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "includeExpired": {
          "type": "boolean"
         },
         "source": {
          "type": "string",
          "enum": [
           "manual",
           "automatic"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PackageAssignmentSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/listMine": {
   "post": {
    "operationId": "packages.listMine",
    "tags": [
     "packages"
    ],
    "summary": "Returns your self-service view: the requestable packages with your status on each, your assignments, and your recent requests.",
    "description": "- **Permission:** `iam:packages:request` on the tenant.\n- **Audited as:** `iam:packages:request`.\n\nEach requestable package comes with its roles and groups named, the approver group's name, and your live\n`assignment` or `pending` request, which is enough to render \"held\", \"awaiting approval\", or \"request\". A package's\nrule is never shown here. `requests` holds your 50 most recent requests. Every requestable package is listed, even\none you lack `iam:packages:request` on, so `request` can still refuse it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "packages": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "assignment": {
                "$ref": "#/components/schemas/PackageAssignmentSummary"
               },
               "pending": {
                "$ref": "#/components/schemas/PackageRequestSummary"
               },
               "approverGroupName": {
                "type": "string"
               },
               "roles": {
                "type": "array",
                "items": {
                 "type": "object",
                 "properties": {
                  "id": {
                   "type": "string"
                  },
                  "name": {
                   "type": "string"
                  }
                 },
                 "required": [
                  "id",
                  "name"
                 ],
                 "additionalProperties": false
                }
               },
               "groups": {
                "type": "array",
                "items": {
                 "type": "object",
                 "properties": {
                  "id": {
                   "type": "string"
                  },
                  "name": {
                   "type": "string"
                  }
                 },
                 "required": [
                  "id",
                  "name"
                 ],
                 "additionalProperties": false
                }
               },
               "name": {
                "type": "string"
               },
               "description": {
                "type": "string"
               },
               "maxDurationMs": {
                "type": "number"
               },
               "requireJustification": {
                "type": "boolean"
               },
               "requestable": {
                "type": "boolean"
               },
               "approverGroupId": {
                "type": "string"
               },
               "managerApproval": {
                "type": "boolean"
               },
               "createdAt": {
                "type": "number"
               },
               "updatedAt": {
                "type": "number"
               },
               "id": {
                "type": "string"
               },
               "tenantId": {
                "type": "string"
               }
              },
              "required": [
               "assignment",
               "pending",
               "roles",
               "groups",
               "name",
               "createdAt",
               "updatedAt",
               "id",
               "tenantId"
              ],
              "additionalProperties": false
             }
            },
            "assignments": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/PackageAssignmentSummary"
             }
            },
            "requests": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/PackageRequestSummary"
             }
            }
           },
           "required": [
            "packages",
            "assignments",
            "requests"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/listRequests": {
   "post": {
    "operationId": "packages.listRequests",
    "tags": [
     "packages"
    ],
    "summary": "Lists package requests for a package or a person, newest first, optionally by status.",
    "description": "- **Permission:** `iam:packages:read` on the package when you pass `packageId`, otherwise on the tenant.\n- **Audited as:** `iam:packages:read`.\n- **Errors:** `INVALID_INPUT` for an unknown `status`; `NOT_FOUND` when `packageId` is not in this tenant.\n\nA pending request past its lapse time is reported, and filtered, as `expired` even before the purge worker marks it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#listrequests"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "approved",
           "denied",
           "cancelled",
           "expired"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PackageRequestSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/previewAutoAssign": {
   "post": {
    "operationId": "packages.previewAutoAssign",
    "tags": [
     "packages"
    ],
    "summary": "Shows what a package rule matches and, for a package, what a reconcile would change, without writing anything.",
    "description": "- **Permission:** `iam:packages:read` on the package (or the tenant without `packageId`), plus `iam:identities:read`\n  on the tenant to evaluate a rule.\n- **Audited as:** `iam:packages:read`.\n- **Errors:** `INVALID_INPUT` or `INVALID_POLICY` for a candidate rule that does not validate; `ACCESS_DENIED`\n  without `iam:identities:read`; `NOT_FOUND` when `packageId` is not in this tenant.\n\nPass a candidate `autoAssign` to test a rule before saving it, `packageId` alone to inspect the stored rule, or\nneither to get only `keys`, the attribute keys a rule may test with their operators. The result counts `matching`,\n`excluded`, and `frozen` (disabled or expired) identities and lists a `sample` of matches (20 by default, at most\n100). With `packageId` it adds the `plan` (how many would be assigned, refreshed, restored, ended, or revoked), the\nfirst changes, whether the `brake` would hold them back, and the rule's current `status`. `warnings` flags clauses\nthat also match service accounts or accept unverified email addresses.\n\n```ts\nconst preview = await iam.api.packages.previewAutoAssign(credential, {\n  tenantId,\n  packageId,\n  autoAssign: {\n    include: [\n      {\n        StringEqualsIgnoreCase: { 'identity.emailDomain': 'acme.com' },\n        Bool: { 'identity.emailVerified': true },\n        StringEquals: { 'principal.kind': 'user' },\n      },\n    ],\n  },\n});\n// preview.plan: { assign, refresh, restore, ending, revoke, manual, keep }\n// preview.brake.grants.trips: true when a scheduled run would hold the grants back\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#previewautoassign"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "autoAssign": {
          "$ref": "#/components/schemas/AutoAssignInput"
         },
         "sample": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AutoAssignPreview"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/reconcile": {
   "post": {
    "operationId": "packages.reconcile",
    "tags": [
     "packages"
    ],
    "summary": "Runs the package-rule reconciler now for the tenant or one package, optionally confirming held-back changes.",
    "description": "- **Permission:** `iam:packages:assign` on the package (or the tenant without `packageId`). `confirm: true` also\n  needs the rights to assign the package by hand.\n- **Audited as:** `iam:packages:assign`; each change as `package:auto-assign`, `package:auto-ending`, or\n  `package:auto-revoke`, and problems as `package:auto-failed`, `package:auto-suspended`, or `package:auto-braked`,\n  all by `deployment-operator`; a confirmation as `package:auto-confirm` by you.\n- **Errors:** `INVALID_INPUT` when `confirm` is given without `packageId` or `limit` is outside 1 to 10000;\n  `INVALID_TRANSITION` when confirming a package that has no rule; `ACCESS_DENIED` or `GRANT_AUTHORITY_REQUIRED` when\n  confirming without the rights.\n\nUse it after a bulk import or a configuration apply (which does not reconcile), or to apply changes the brake held\nback. Without `packageId` it covers every rule package in the tenant, plus packages whose rule was cleared but that\nstill have automatic holders. Removals run first, then additions, each change in its own transaction, at most\n`limit` changes (1000 by default).\nWithout `confirm`, a run holds back more than the rule's `maxGrants` (100) new grants or `maxRemovals` (25) removals\nper package and reports them in `braked`. `confirm: true` approves the package's planned counts for a day and applies\nthem in this run. Failures for one person, such as a separation-of-duties conflict, are reported in `failed` and\nnever stop the run; `truncated: true` means the budget ran out, so run again.\n\n```ts\n// A scheduled run held back 140 new grants after a bulk import. Review, then confirm.\nconst result = await iam.api.packages.reconcile(credential, { tenantId, packageId, confirm: true });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#reconcile"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "confirm": {
          "type": "boolean"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageReconcileResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/request": {
   "post": {
    "operationId": "packages.request",
    "tags": [
     "packages"
    ],
    "summary": "Asks for a requestable package for yourself; approvers are notified and one of them decides.",
    "description": "- **Permission:** `iam:packages:request` on the package, from an ordinary session of the tenant.\n- **Audited as:** `iam:packages:request`, plus `package:request` with the lapse time, requested end, and\n  justification.\n- **Errors:** `INVALID_TRANSITION` when the package is not requestable, or it names approvers but none could act\n  (an empty approver group and no active manager); `CONFLICT` when you already hold the package or a request of\n  yours is still pending; `INVALID_INPUT` from a role session or another tenant's session, or when the end or\n  justification the package requires is missing or out of range; `IMPERSONATION_RESTRICTED` from an impersonation\n  session.\n\n`expiresAt` is the end you want the access to have, and `justification` the reason, both under the same rules as\n`assign`. The request lapses after the tenant's `approvalLifetimeMs`, or at the end you asked for if that comes\nfirst. When the deployment sends email, the approver group's members and, with `managerApproval`, your manager\nreceive a `package-request` message. Grant `iam:packages:request` through a group everyone belongs to, like\n`iam:bindings:activate` for [just-in-time elevation](/docs/guides/privileged-access/elevation).\n\n```ts\nconst pending = await iam.api.packages.request(memberCredential, {\n  tenantId,\n  packageId,\n  expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000,\n  justification: 'On call for the payments team next month',\n});\n// pending.status === 'pending'; pending.expiresAt is when the request lapses\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#request"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         },
         "justification": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "packageId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PackageRequestSummary"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/revoke": {
   "post": {
    "operationId": "packages.revoke",
    "tags": [
     "packages"
    ],
    "summary": "Removes a package from a person, taking away exactly the bindings and memberships the assignment still owns.",
    "description": "- **Permission:** `iam:packages:assign` on the package.\n- **Audited as:** `iam:packages:assign`, plus `package:revoke` with the removed counts.\n- **Errors:** `NOT_FOUND` when the package is not assigned to the person; `INVALID_TRANSITION` for an automatic\n  assignment while the package still has its rule; `INVARIANT_VIOLATION` when the removal would break an enforced\n  invariant.\n\nThe assignment owns its records whichever authority issued them, so revoking needs no grant authority. Records\ntaken over by hand stay, and a shared membership passes to another of the person's packages that includes the\ngroup. To stop a rule from giving someone the package, exclude them in the rule (for example\n`exclude: [{ StringEquals: { 'principal.id': identityId } }]`) or change their attributes.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "packageId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "packageId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "bindings": {
             "type": "number"
            },
            "memberships": {
             "type": "number"
            },
            "revoked": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "bindings",
            "memberships",
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/packages/update": {
   "post": {
    "operationId": "packages.update",
    "tags": [
     "packages"
    ],
    "summary": "Changes a package's name, description, contents, request settings, or rule.",
    "description": "- **Permission:** `iam:packages:update` on the package. Setting, changing, or clearing a rule, or changing a rule\n  package's roles or groups, also needs `iam:packages:assign` and, except when clearing, the rule-owner checks of\n  `create`.\n- **Audited as:** `iam:packages:update`, plus `package:auto-rule` (`set`, `change`, `owner`, `contents`, or `clear`)\n  when the rule changes.\n- **Errors:** `CONFLICT` for a name another package uses; `INVALID_INPUT` when the rule no longer fits new contents,\n  `keepAutomaticAssignments` is used without clearing a rule or with more than 5000 automatic holders, or\n  `maxDurationMs` is set on a rule package; plus the validation and owner-check errors of `create`.\n\nManual assignments keep what they were given; contents changes apply to future assignments only. Automatic\nassignments follow the package: the reconcile that runs after the save (up to 200 changes, the rest at the next\nrun) adds and removes their records. Changing a rule package's roles or groups makes you the rule's owner, so a\nrole added later is always granted under the authority of the person who added it. `autoAssign: null` clears the\nrule, and reconciliation then removes the automatic holders unless you pass `keepAutomaticAssignments: true`, which\nturns them into manual assignments. Pass `null` to clear `description`, `maxDurationMs`, or `approverGroupId`.\n\nTightening a package cancels the pending requests it no longer allows, with the reason as the note: turning\n`requestable` off, requiring a justification a request lacks, or capping the duration below what a request asked for.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/packages#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "type": "object",
          "properties": {
           "tenantId": {
            "type": "string"
           },
           "packageId": {
            "type": "string"
           }
          },
          "required": [
           "tenantId",
           "packageId"
          ],
          "additionalProperties": false
         },
         {
          "$ref": "#/components/schemas/PackageUpdate"
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/PublicPackage"
            },
            {
             "type": "object",
             "properties": {
              "reconcile": {
               "$ref": "#/components/schemas/PackageReconcileResult"
              }
             },
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/create": {
   "post": {
    "operationId": "policies.create",
    "tags": [
     "policies"
    ],
    "summary": "Stores a new policy document at version 1 under your grant authority.",
    "description": "- **Permission:** `iam:policies:create` on the tenant, plus an active grant authority.\n- **Audited as:** `iam:policies:create`.\n- **Errors:** `INVALID_POLICY`, `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE` when the document does not validate\n  against the catalog; `GRANT_AUTHORITY_REQUIRED` when you hold no active grant authority; `LIMIT_EXCEEDED` when the\n  tenant's plan limit for policies is reached; `INVALID_INPUT` for an empty name or a description over 512\n  characters.\n\nThe new policy grants nothing until a role attaches it with\n[`roles.create`](/docs/reference/api/roles#create) or [`roles.update`](/docs/reference/api/roles#update). Try a\ndraft with `test` first.\n\n```ts\nconst readOwn = await iam.api.policies.create(credential, {\n  tenantId,\n  name: 'Read own documents',\n  document: {\n    version: 1,\n    statements: [\n      {\n        sid: 'OwnedDocuments',\n        effect: 'allow',\n        actions: ['documents:read'],\n        resources: ['document/*'],\n        conditions: { StringEquals: { 'resource.ownerId': '${principal.id}' } },\n      },\n    ],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PolicyInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Policy"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/delete": {
   "post": {
    "operationId": "policies.delete",
    "tags": [
     "policies"
    ],
    "summary": "Deletes a policy that no role attaches.",
    "description": "- **Permission:** `iam:policies:delete` on the policy, and the policy's grant authority (or root).\n- **Audited as:** `iam:policies:delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) while any role still attaches the policy; `PROTECTED_RESOURCE` for the Owner\n  policy; `ACCESS_DENIED` when another administrator's authority created it; `NOT_FOUND`.\n\nDetach the policy first by updating each role's `policyIds`. The refusal exists so that deleting a policy never\nsilently removes access from the roles built on it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "policyId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "policyId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/effectiveActions": {
   "post": {
    "operationId": "policies.effectiveActions",
    "tags": [
     "policies"
    ],
    "summary": "Lists which actions one identity could perform on one resource, with a reason per action.",
    "description": "- **Permission:** `iam:policies:simulate` on the identity (`iam/{identityId}`).\n- **Audited as:** `iam:policies:simulate`.\n- **Errors:** `INVALID_INPUT` for more than 200 `actions`; `INVALID_ACTION` when one of them is not in the catalog;\n  `NOT_FOUND` when the identity is not in this tenant or a managed resource is not registered;\n  `RESOURCE_RESOLVER_REQUIRED`.\n\nWithout `actions`, every action in the catalog is checked: the built-in `iam:*` actions, your product's and plugins'\nactions, and tenant-defined ones. The identity's grants are loaded once, so this is cheaper than calling `simulate`\nper action. The result has `allowed` (the sorted action names) and `results` (each action with `allowed` and\n`reason`), which is what a \"what can this person do here?\" panel needs.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#effectiveactions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "resource": {
          "type": "object",
          "properties": {
           "type": {
            "type": "string"
           },
           "id": {
            "type": "string"
           }
          },
          "required": [
           "type",
           "id"
          ],
          "additionalProperties": false
         },
         "actions": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "assumeMfa": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "resource"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "allowed": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "results": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "action": {
                "type": "string"
               },
               "allowed": {
                "type": "boolean"
               },
               "reason": {
                "type": "string"
               }
              },
              "required": [
               "action",
               "allowed",
               "reason"
              ],
              "additionalProperties": false
             }
            }
           },
           "required": [
            "allowed",
            "results"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/get": {
   "post": {
    "operationId": "policies.get",
    "tags": [
     "policies"
    ],
    "summary": "Returns one policy with its current document and version.",
    "description": "- **Permission:** `iam:policies:read` on the policy.\n- **Audited as:** `iam:policies:read`.\n- **Errors:** `NOT_FOUND` when the policy is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "policyId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "policyId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Policy"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/list": {
   "post": {
    "operationId": "policies.list",
    "tags": [
     "policies"
    ],
    "summary": "Lists every policy in the tenant at its current version, including the protected Owner policy.",
    "description": "- **Permission:** `iam:policies:read` on the tenant.\n- **Audited as:** `iam:policies:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Policy"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/listVersions": {
   "post": {
    "operationId": "policies.listVersions",
    "tags": [
     "policies"
    ],
    "summary": "Returns a policy's full history, oldest first, ending with the current version.",
    "description": "- **Permission:** `iam:policies:read` on the policy.\n- **Audited as:** `iam:policies:read`.\n- **Errors:** `NOT_FOUND` when the policy is not in this tenant.\n\nEach entry carries the `document`, `name`, and `version` as they were. Archived entries have their own record `id`\nand point back to the policy through `policyId`, so pick a version for `restoreVersion` by its `version` number.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#listversions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "policyId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "policyId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Policy"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/restoreVersion": {
   "post": {
    "operationId": "policies.restoreVersion",
    "tags": [
     "policies"
    ],
    "summary": "Rolls a policy back to an earlier document by saving that document as a new version.",
    "description": "- **Permission:** `iam:policies:update` on the policy, and the policy's grant authority (or root).\n- **Audited as:** `iam:policies:update`.\n- **Errors:** `INVALID_INPUT` when `version` is not between 1 and the current version, or is the current version;\n  `NOT_FOUND` when that version is not in the history; `INVALID_ACTION` or `INVALID_RESOURCE_TYPE` when the old\n  document no longer fits the catalog; `PROTECTED_RESOURCE`; `ACCESS_DENIED`; `INVARIANT_VIOLATION`.\n\nHistory is never rewritten: restoring version 3 of a policy at version 7 saves version 8 with version 3's\ndocument. Only the document is restored; the name and description stay as they are. The old document is validated\nagain because actions or resource types may have been removed since it was written.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#restoreversion"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "policyId": {
          "type": "string"
         },
         "version": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "policyId",
         "version"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "document": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "version": {
             "type": "number"
            },
            "name": {
             "type": "string"
            },
            "description": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "document",
            "version",
            "name",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/simulate": {
   "post": {
    "operationId": "policies.simulate",
    "tags": [
     "policies"
    ],
    "summary": "Explains the decision one identity would get for one action on one resource, without creating a session or granting anything.",
    "description": "- **Permission:** `iam:policies:simulate` on the identity (`iam/{identityId}`).\n- **Audited as:** `iam:policies:simulate`.\n- **Errors:** `NOT_FOUND` when the identity is not in this tenant or a managed resource is not registered;\n  `RESOURCE_RESOLVER_REQUIRED` for an application-owned resource type when no `resolveResource` is configured;\n  `RESOURCE_MISMATCH` when the resolver returns a record of another tenant or resource.\n\nUse it when support asks \"why can't this person open this?\". Unlike the public authorization calls, the result\nincludes `matched`, the statements that decided. An action missing from the catalog returns `allowed: false` with\nreason `UNKNOWN_ACTION` rather than an error. For `iam:*` actions, pass platform resources such as\n`{ type: 'iam', id: roleId }`.\n\n```ts\nconst decision = await iam.api.policies.simulate(credential, {\n  tenantId,\n  identityId: alice.id,\n  action: 'invoices:approve',\n  resource: { type: 'invoice', id: 'inv_2041' },\n  assumeMfa: true,\n});\n// { allowed: false, reason: 'explicit-deny', matched: [...] }\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#simulate"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "action": {
          "type": "string"
         },
         "resource": {
          "type": "object",
          "properties": {
           "type": {
            "type": "string"
           },
           "id": {
            "type": "string"
           }
          },
          "required": [
           "type",
           "id"
          ],
          "additionalProperties": false
         },
         "assumeMfa": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "action",
         "resource"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Decision"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/test": {
   "post": {
    "operationId": "policies.test",
    "tags": [
     "policies"
    ],
    "summary": "Evaluates an unsaved policy document against one action, resource, and context, for policy editors.",
    "description": "- **Permission:** `iam:policies:simulate` on the tenant.\n- **Audited as:** `iam:policies:simulate`.\n- **Errors:** `INVALID_POLICY`, `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE` when the document does not validate;\n  `INVALID_INPUT` when `context` has more than 200 keys or `resource` is over 2048 characters.\n\nOnly the document is evaluated: no identity, roles, bindings, or boundaries are involved, so it answers \"does this\ndocument say what I mean?\". `resource` is a `type/id` string. The context starts with the keys a session issued now\nwould carry (`resource.tenantId` and `principal.tenantId` set to the tenant, `principal.sessionId`,\n`principal.tokenIssueTime`, `principal.authTime`, `principal.sessionTagKeys`, and `request.time`); your `context`\nadds keys or overrides them. Add `'principal.mfa': true` to test an MFA condition and `principal.id` to resolve\n`${principal.id}` variables. The result is a full decision with `matched`, the statements that matched as\n`grant:{index}:{sid}` (the statement's position when it has no `sid`).\n\n```ts\nconst decision = await iam.api.policies.test(credential, {\n  tenantId,\n  document: draft,\n  action: 'documents:read',\n  resource: 'document/plan-2027',\n  context: { 'principal.id': 'usr_123', 'resource.ownerId': 'usr_123' },\n});\n// { allowed: true, reason: 'allowed', matched: ['grant:0:OwnedDocuments'] }\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#test"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "document": {
          "$ref": "#/components/schemas/PolicyDocument"
         },
         "action": {
          "type": "string"
         },
         "resource": {
          "type": "string"
         },
         "context": {
          "type": "object",
          "properties": {},
          "additionalProperties": {}
         }
        },
        "required": [
         "tenantId",
         "document",
         "action",
         "resource"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Decision"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/update": {
   "post": {
    "operationId": "policies.update",
    "tags": [
     "policies"
    ],
    "summary": "Saves a new version of a policy's document, name, or description, keeping the previous version in its history.",
    "description": "- **Permission:** `iam:policies:update` on the policy, and the grant authority the policy was created under (or\n  root).\n- **Audited as:** `iam:policies:update`.\n- **Errors:** `VERSION_CONFLICT` (409) when `version` is not the current version; `ACCESS_DENIED` when another\n  administrator's authority created the policy; `PROTECTED_RESOURCE` for the Owner policy; `INVALID_INPUT` when\n  none of `document`, `name`, or `description` is given; `INVALID_POLICY`, `INVALID_ACTION`, or\n  `INVALID_RESOURCE_TYPE` for the new document; `GRANT_AUTHORITY_REQUIRED`; `NOT_FOUND`; `INVARIANT_VIOLATION` when\n  the change would newly break an enforced [access invariant](/docs/reference/api/invariants).\n\nPass the `version` you read. Renaming also creates a new version. The change applies at the next request to every\nrole that attaches the policy, so preview it first with [`impact.preview`](/docs/reference/api/impact#preview).\n\n```ts\nconst current = await iam.api.policies.get(credential, { tenantId, policyId });\nawait iam.api.policies.update(credential, {\n  tenantId,\n  policyId,\n  version: current.version,\n  document: {\n    version: 1,\n    statements: [\n      ...current.document.statements,\n      { effect: 'deny', actions: ['documents:delete'], resources: ['document/*'] },\n    ],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PolicyUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Policy"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/policies/whoCan": {
   "post": {
    "operationId": "policies.whoCan",
    "tags": [
     "policies"
    ],
    "summary": "Lists every active identity that could perform an action on a resource, with the reason, for access reviews.",
    "description": "- **Permission:** `iam:policies:simulate` on the tenant.\n- **Audited as:** `iam:policies:simulate`.\n- **Errors:** `INVALID_ACTION` for an action missing from the catalog; `INVALID_INPUT` for a `kind` other than\n  `user` or `service`, a `limit` outside 1 to 1000, or an invalid `offset`; `NOT_FOUND` when a managed resource is\n  not registered; `RESOURCE_RESOLVER_REQUIRED`.\n\nEvery active identity of the tenant is evaluated, people and service accounts alike; `kind` narrows to one. The\nresult has `identities` (id, name, email, kind, and the decision reason) and `total`, the number of matches before\npaging with `limit` (100 by default) and `offset`. Grants are loaded per identity, so the cost grows with the\ndirectory: use it on review screens, not on every request.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/policies#whocan"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "action": {
          "type": "string"
         },
         "resource": {
          "type": "object",
          "properties": {
           "type": {
            "type": "string"
           },
           "id": {
            "type": "string"
           }
          },
          "required": [
           "type",
           "id"
          ],
          "additionalProperties": false
         },
         "kind": {
          "type": "string",
          "enum": [
           "user",
           "service"
          ]
         },
         "assumeMfa": {
          "type": "boolean"
         },
         "limit": {
          "type": "number"
         },
         "offset": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "action",
         "resource"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "identities": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/ReviewMatch"
             }
            },
            "total": {
             "type": "number"
            }
           },
           "required": [
            "identities",
            "total"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/relationships/create": {
   "post": {
    "operationId": "relationships.create",
    "tags": [
     "relationships"
    ],
    "summary": "Gives an identity or group a declared relation on one resource, optionally until a given time.",
    "description": "- **Permission:** `iam:relationships:create` on `iam/{type}/{id}`.\n- **Audited as:** `iam:relationships:create`, on `{type}/{id}`.\n- **Errors:** `INVALID_RESOURCE_TYPE` when the type is not declared; `INVALID_INPUT` when the relation is not declared\n  for the type, the subject type is not `identity` or `group`, or `expiresAt` is not in the future (at most ten years\n  out); `NOT_FOUND` when a managed resource is not registered, the identity is not in this tenant or was deleted, or\n  the group is not in this tenant; `INVARIANT_VIOLATION` when an enforced\n  [access invariant](/docs/guides/governance/change-safety) would newly fail.\n\nResources of managed types must be registered first; resources of application-owned types are accepted as named.\nCreating a tuple that already exists replaces it instead of failing: its `expiresAt` becomes the one you pass (none\nmakes it permanent) and the caller is recorded as `createdBy`. Use `expiresAt` for time-boxed sharing, such as giving\nan auditor `viewer` on a folder for a week.\n\n```ts\nawait iam.api.relationships.create(credential, {\n  tenantId,\n  type: 'folder',\n  id: 'plans',\n  relation: 'viewer',\n  subjectType: 'group',\n  subjectId: designGroupId,\n  expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/relationships#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         },
         "relation": {
          "type": "string"
         },
         "subjectType": {
          "type": "string",
          "enum": [
           "identity",
           "group"
          ]
         },
         "subjectId": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "type",
         "id",
         "relation",
         "subjectType",
         "subjectId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Relationship"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/relationships/delete": {
   "post": {
    "operationId": "relationships.delete",
    "tags": [
     "relationships"
    ],
    "summary": "Removes one relationship tuple, ending the access it gave.",
    "description": "- **Permission:** `iam:relationships:delete` on the tuple's resource, `iam/{type}/{id}`.\n- **Audited as:** `iam:relationships:delete`, on `{type}/{id}`.\n- **Errors:** `NOT_FOUND` when the tuple is not in this tenant; `INVARIANT_VIOLATION` when an enforced access\n  invariant would newly fail.\n\nPass the tuple's `id`, as returned by [`create`](#create) or [`list`](#list). The change applies to the next\ndecision.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/relationships#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "relationshipId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "relationshipId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/relationships/list": {
   "post": {
    "operationId": "relationships.list",
    "tags": [
     "relationships"
    ],
    "summary": "Lists relationship tuples of one resource, one subject, one type, or the whole tenant, newest first.",
    "description": "- **Permission:** `iam:relationships:read` on `iam/{type}/{id}` when `type` and `id` are given, on `iam/{type}/*`\n  when only `type` is, otherwise on `iam/*`.\n- **Audited as:** `iam:relationships:read`.\n- **Errors:** `INVALID_INPUT` for a subject type other than `identity` or `group`.\n\nFilter by `type`, `id`, `relation`, `subjectType`, and `subjectId` in any combination: \"who can see this folder\" is\n`{ type, id }`, and \"what has been shared with this group\" is `{ subjectType: 'group', subjectId }`. Expired tuples\nare left out unless `includeExpired` is `true`. Because the permission is checked on the resource, an owner allowed to\nread relationships on their own folder can review who it is shared with.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/relationships#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         },
         "relation": {
          "type": "string"
         },
         "subjectType": {
          "type": "string",
          "enum": [
           "identity",
           "group"
          ]
         },
         "subjectId": {
          "type": "string"
         },
         "includeExpired": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Relationship"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/reports/access": {
   "post": {
    "operationId": "reports.access",
    "tags": [
     "reports"
    ],
    "summary": "Returns the tenant's access report: identities and grants ending soon, live elevations, pending requests, and unused or expiring API keys.",
    "description": "- **Permission:** `iam:identities:read` on the tenant. The bindings section also needs `iam:bindings:read` and the\n  credentials section `iam:credentials:read` on the tenant; without them the section is left out, not refused.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `INVALID_INPUT` when `withinMs` or `unusedForMs` is negative or longer than ten years.\n\n`withinMs` (default 30 days) is the look-ahead window for things that end or start, and `unusedForMs` (default 30\ndays) is how long an API key must go unused to be listed. The report has three sections:\n\n- **`identities`**: how many identities the tenant has (deleted ones excluded) and how many are disabled, plus\n  people and service accounts whose `expiresAt` falls within the window. An identity already past its deadline\n  stays listed with `expired: true` until the retention worker disables it.\n- **`bindings`**: how many unexpired bindings there are, and how many of them are eligible or limited to an access\n  window; bindings that expire within the window; future-dated bindings that start within it; temporary group\n  memberships that end within it; live just-in-time activations with their justification; and the number of\n  activation requests awaiting a decision.\n- **`credentials`**: the number of unexpired API keys, keys not used for `unusedForMs` (never-used keys count from\n  their creation), and keys that expire within the window.\n\n`omitted` names the sections the caller could not read, so a directory administrator without\n`iam:credentials:read` still gets the rest. For email delivery to tenant owners, schedule\n[`iam.sendAccessDigest()`](/docs/reference/api#sendaccessdigest) instead; the `report`\n[CLI command](/docs/reference/cli#report) prints the same report.\n\n```ts\nconst report = await iam.api.reports.access(credential, { tenantId, withinMs: 14 * 24 * 60 * 60 * 1000 });\nfor (const binding of report.bindings?.expiring ?? [])\n  console.log(`${binding.subjectName ?? binding.subjectId} loses ${binding.roleName ?? binding.roleId}`);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/reports#access"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "withinMs": {
          "type": "number"
         },
         "unusedForMs": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/AccessReport"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resourceTypes/delete": {
   "post": {
    "operationId": "resourceTypes.delete",
    "tags": [
     "resourceTypes"
    ],
    "summary": "Deletes a tenant-defined resource type and its actions.",
    "description": "- **Permission:** `iam:resource-types:delete` on the tenant.\n- **Audited as:** `iam:resource-types:delete`.\n- **Errors:** `NOT_FOUND` when no tenant-defined type has that name (platform types cannot be deleted);\n  `RESOURCE_IN_USE` while resources of the type are registered, relationship tuples reference it, another type names it\n  as its parent, or a stored policy or inline role document still names one of its actions.\n\nThe in-use checks keep deletion from silently changing access: remove the resources, relationships, child types, and\npolicy references first.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resource-types#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resourceTypes/get": {
   "post": {
    "operationId": "resourceTypes.get",
    "tags": [
     "resourceTypes"
    ],
    "summary": "Returns one resource type, platform or tenant-defined, with its actions, attributes, relations, and parent.",
    "description": "- **Permission:** `iam:resource-types:read` on the tenant.\n- **Audited as:** `iam:resource-types:read`.\n- **Errors:** `NOT_FOUND` when the name is unknown to this tenant.\n\n`source` is `platform` or `tenant`, and `managed` says whether resources of the type are registered with IAM or\nresolved by the application.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resource-types#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CatalogResourceType"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resourceTypes/list": {
   "post": {
    "operationId": "resourceTypes.list",
    "tags": [
     "resourceTypes"
    ],
    "summary": "Lists every resource type the tenant can use: platform types first, then the tenant's own.",
    "description": "- **Permission:** `iam:resource-types:read` on the tenant.\n- **Audited as:** `iam:resource-types:read`.\n\nUse it to build policy editors and resource pickers. Platform types include application-owned ones (`managed: false`),\nwhich exist for validation and documentation but are not registered through the API.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resource-types#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/CatalogResourceType"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resourceTypes/register": {
   "post": {
    "operationId": "resourceTypes.register",
    "tags": [
     "resourceTypes"
    ],
    "summary": "Defines a new resource type for the tenant, and registers its actions in the same transaction.",
    "description": "- **Permission:** `iam:resource-types:create` on the tenant.\n- **Audited as:** `iam:resource-types:create`.\n- **Errors:** `CATALOG_LOCKED` (403) when the deployment does not allow tenant-defined types;\n  `INVALID_RESOURCE_TYPE` for an invalid or reserved name, or a `parent` that is not an existing managed type;\n  `CONFLICT` when the tenant already has a type with that name; `INVALID_INPUT` for invalid attributes or relations;\n  `INVALID_ACTION` for an invalid verb.\n\n```ts\nawait iam.api.resourceTypes.register(credential, {\n  tenantId,\n  name: 'contract',\n  description: 'Customer contracts',\n  actions: ['read', 'approve'], // registers contract:read and contract:approve\n  attributes: { value: 'number', region: 'string' },\n  relations: ['owner', 'reviewer'],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resource-types#register"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ResourceTypeInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CatalogResourceType"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resourceTypes/update": {
   "post": {
    "operationId": "resourceTypes.update",
    "tags": [
     "resourceTypes"
    ],
    "summary": "Changes a tenant-defined type's description, attribute schema, or relations, and adds action verbs.",
    "description": "- **Permission:** `iam:resource-types:update` on the tenant.\n- **Audited as:** `iam:resource-types:update`.\n- **Errors:** `NOT_FOUND` when no tenant-defined type has that name; `INVALID_INPUT` when a registered resource would\n  no longer match the new attribute schema; `RESOURCE_IN_USE` when a relation you drop is still held by a relationship\n  tuple; `CATALOG_LOCKED` when adding verbs while tenant-defined actions are disabled.\n\n`attributes` and `relations` replace the whole list. `actions` only adds: verbs already registered are kept and\nrepeated ones are skipped. To remove a verb, use [`actions.unregister`](/docs/reference/api/actions#unregister). The\nname and parent cannot change. The schema check exists because conditions on an attribute would otherwise silently\nstop matching resources that no longer fit it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resource-types#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ResourceTypeUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CatalogResourceType"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/delete": {
   "post": {
    "operationId": "resources.delete",
    "tags": [
     "resources"
    ],
    "summary": "Removes a registered resource and every relationship tuple on it.",
    "description": "- **Permission:** `iam:resources:delete` on `iam/{type}/{id}`.\n- **Audited as:** `iam:resources:delete`, on `{type}/{id}`.\n- **Errors:** `NOT_FOUND` when the resource is not registered; `RESOURCE_IN_USE` when registered child resources\n  still point at it; `INVARIANT_VIOLATION` when an enforced\n  [access invariant](/docs/guides/governance/change-safety) would newly fail.\n\nDelete children first: a parent cannot be removed while resources registered under it exist. Once deleted, any\ndecision about the resource sees no attributes, owner, or relations.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "type",
         "id"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/get": {
   "post": {
    "operationId": "resources.get",
    "tags": [
     "resources"
    ],
    "summary": "Returns the registration of one resource.",
    "description": "- **Permission:** `iam:resources:read` on `iam/{type}/{id}`.\n- **Audited as:** `iam:resources:read`, on `{type}/{id}`.\n- **Errors:** `NOT_FOUND` when the resource is not registered.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "type",
         "id"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ResourceRecord"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/list": {
   "post": {
    "operationId": "resources.list",
    "tags": [
     "resources"
    ],
    "summary": "Lists registered resources, optionally of one type, under one parent, or owned by one identity.",
    "description": "- **Permission:** `iam:resources:read` on `iam/{type}/*` when `type` is given, otherwise on `iam/*`.\n- **Audited as:** `iam:resources:read`.\n- **Errors:** `INVALID_INPUT` when `limit` is outside 1 to 1000.\n\nResults are sorted by `type/id`, so pages are stable and meaningful; `limit` defaults to 100, with `offset` for the\nnext page. `parentId` is the parent's own identifier. This is an administrative listing of what exists. To list what a\nperson may act on, use [`listAccessible`](/docs/reference/api#listaccessible).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "parentId": {
          "type": "string"
         },
         "ownerId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         },
         "offset": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ResourceRecord"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/register": {
   "post": {
    "operationId": "resources.register",
    "tags": [
     "resources"
    ],
    "summary": "Registers a managed resource with its attributes, owner, and parent.",
    "description": "- **Permission:** `iam:resources:create` on `iam/{type}/{id}`.\n- **Audited as:** `iam:resources:create`, on `{type}/{id}`.\n- **Errors:** `INVALID_RESOURCE_TYPE` when the type is unknown or application-owned; `CONFLICT` when the resource is\n  already registered; `INVALID_INPUT` for undeclared or wrongly typed attributes, a missing `parentId` on a type that\n  declares a parent, or a `parentId` on a type that does not; `NOT_FOUND` when the parent is not registered or\n  `ownerId` is not an identity of the tenant (or was deleted); `LIMIT_EXCEEDED` at the tenant's resource limit.\n\nBecause the permission is checked on the resource's own address, policies can limit who registers what, for example\n`iam/project/*` for project administrators. Register a resource when your product creates it, in the same request, so\naccess rules apply from the first moment. The parent cannot be changed later.\n\n```ts\nawait iam.api.resources.register(credential, {\n  tenantId,\n  type: 'task',\n  id: 'task_812',\n  parentId: 'proj_apollo', // a registered `project`, because `task` declares `parent: 'project'`\n  ownerId: identityId,\n  attributes: { priority: 2 }, // the type declares `priority: 'number'`\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#register"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         },
         "attributes": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "description": "Any JSON value"
          }
         },
         "parentId": {
          "type": "string"
         },
         "ownerId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "type",
         "id"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ResourceRecord"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/registerMany": {
   "post": {
    "operationId": "resources.registerMany",
    "tags": [
     "resources"
    ],
    "summary": "Registers up to 100 managed resources in one transaction, either all of them or none.",
    "description": "- **Permission:** `iam:resources:create` on `iam/{type}/{id}` for every item.\n- **Audited as:** `iam:resources:create`, once per item.\n- **Errors:** `INVALID_INPUT` for an empty list or more than 100 items; `ACCESS_DENIED` naming the first item the\n  caller may not register (only that denial is recorded and nothing is written); `LIMIT_EXCEEDED` when the whole batch\n  does not fit the tenant's limit; any error [`register`](#register) raises for one item rejects the batch.\n\nEvery item is authorized before anything is written. Use it to import existing data or to register a parent and its\nchildren together (list the parent first).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#registermany"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "resources": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/ResourceInput"
          }
         }
        },
        "required": [
         "tenantId",
         "resources"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "resources": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/ResourceRecord"
             }
            }
           },
           "required": [
            "resources"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/resources/update": {
   "post": {
    "operationId": "resources.update",
    "tags": [
     "resources"
    ],
    "summary": "Replaces a registered resource's attributes, or changes or clears its owner.",
    "description": "- **Permission:** `iam:resources:update` on `iam/{type}/{id}`.\n- **Audited as:** `iam:resources:update`, on `{type}/{id}`.\n- **Errors:** `NOT_FOUND` when the resource is not registered or the new owner is not in this tenant;\n  `INVALID_INPUT` for undeclared or wrongly typed attributes; `INVALID_RESOURCE_TYPE`; `INVARIANT_VIOLATION` when an\n  enforced access invariant would newly fail.\n\n`attributes` replaces the whole attribute set, so send every attribute you want to keep. `ownerId: null` removes the\nowner. Keep registrations current when the underlying record changes: a condition such as\n`Bool: { 'resource.archived': false }` sees only what was last registered. Offboarding a person with\n[`identities.offboard`](/docs/reference/api/identities#offboard) can transfer the resources they own to a successor.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/resources#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         },
         "attributes": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "description": "Any JSON value"
          }
         },
         "ownerId": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "type",
         "id"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ResourceRecord"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/apply": {
   "post": {
    "operationId": "roleMining.apply",
    "tags": [
     "roleMining"
    ],
    "summary": "Carries out a group-binding or redundant-binding suggestion: binds the role to the group once and removes the direct bindings it replaces.",
    "description": "- **Permission:** `iam:analysis:update` on `iam/analysis/{suggestionId}`, plus `iam:bindings:create` on the role\n  (for a group binding) and `iam:bindings:delete` on each removed binding. The bindings move under the grant\n  authority they already use, so you must hold that authority or be a root administrator.\n- **Audited as:** `iam:analysis:update`.\n- **Errors:** `NOT_FOUND` when the suggestion no longer holds (list suggestions again); `INVALID_INPUT` for a\n  `bundle` or `duplicate-roles` suggestion, or a group binding whose direct bindings come from different grant\n  authorities (`applicable: false`); `ACCESS_DENIED` without the binding rights or the authority;\n  `INVARIANT_VIOLATION` when an enforced [access invariant](/docs/guides/governance/change-safety) would newly\n  break.\n\nThe suggestion is recomputed inside the transaction, so it applies only while the condition still holds, and\neither every binding change happens or none does. Pass the same `minIdentities` and `minRoles` you used with\n`suggest`: the suggestion is looked up again with those settings, and different settings can make it disappear.\n\nBundles are not applied here. Turn a bundle into an access package with\n[`packages.create`](/docs/reference/api/packages#create), and merge duplicate roles by moving bindings to one\nrole and deleting the others.\n\n```ts\nconst { suggestions } = await iam.api.roleMining.suggest(credential, { tenantId, kinds: ['redundant-binding'] });\nconst result = await iam.api.roleMining.apply(credential, { tenantId, suggestionId: suggestions[0].id });\n// result.removedBindingIds: the direct bindings that were deleted\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#apply"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "suggestionId": {
          "type": "string"
         },
         "minIdentities": {
          "type": "number"
         },
         "minRoles": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "suggestionId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "applied": {
             "$ref": "#/components/schemas/RoleSuggestionKind"
            },
            "createdBindingId": {
             "type": "string"
            },
            "removedBindingIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            }
           },
           "required": [
            "applied",
            "createdBindingId",
            "removedBindingIds"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/outliers": {
   "post": {
    "operationId": "roleMining.outliers",
    "tags": [
     "roleMining"
    ],
    "summary": "Finds people whose roles differ from their peers': roles few peers hold and roles most peers hold that the person lacks.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `INVALID_INPUT` when `peerBy` is neither `manager` nor `attribute:NAME` for a declared identity\n  attribute, when `threshold` or `commonShare` is not above 0 and at most 1, or when `minPeers` is out of range.\n\nPeers are people who share a manager (`peerBy: 'manager'`, the default) or the same value of a declared identity\nattribute (`peerBy: 'attribute:department'`). A role is **unusual** when fewer than `threshold` (default 0.25) of\nthe person's peers hold it, which often means access that outlived a move. A role is **missing** when at least\n`commonShare` (default 0.8) of the peers hold it, which often means a joiner who still lacks something. Peer\ngroups with fewer than `minPeers` other people (default 3) are skipped. Eligible (just-in-time) bindings count as\nheld.\n\n```ts\nconst { outliers } = await iam.api.roleMining.outliers(credential, {\n  tenantId,\n  peerBy: 'attribute:department',\n  threshold: 0.2,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#outliers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "peerBy": {
          "type": "string"
         },
         "threshold": {
          "type": "number"
         },
         "commonShare": {
          "type": "number"
         },
         "minPeers": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PeerOutlierResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/reviewRecommendations": {
   "post": {
    "operationId": "roleMining.reviewRecommendations",
    "tags": [
     "roleMining"
    ],
    "summary": "Suggests a keep or revoke decision, with a reason, for every item of an access-certification campaign.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `NOT_FOUND` when the campaign is not in this tenant; `INVALID_INPUT` when `unusedDays` is outside 1\n  to 3650.\n\nEach recommendation is based on evidence. It is `revoke` when the account is disabled, expired, or gone\n(`basis: 'status'`). Otherwise, when recorded usage covers the last `unusedDays` (default 90), it is `keep` if the\nperson used any of the role's actions in that window and `revoke` if not (`basis: 'usage'`). Before usage covers\nthe window, the person's last sign-in decides (`basis: 'sign-in'`). Items for group bindings, and service accounts without usage\ndata, get `none`. Reviewers still decide; the console shows the suggestion beside each open item.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#reviewrecommendations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "campaignId": {
          "type": "string"
         },
         "unusedDays": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "campaignId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ReviewRecommendationsResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/rightSize": {
   "post": {
    "operationId": "roleMining.rightSize",
    "tags": [
     "roleMining"
    ],
    "summary": "Lists every live binding whose holder used none or only some of the role's actions in a window, plus, per role, the actions nobody used.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `INVALID_INPUT` when `unusedDays` is outside 1 to 3650.\n\nThis is least-privilege right-sizing. An entry is `unused` when the holder used none of the role's actions within\n`unusedDays` (default 90) and `partial` when they used some. Per role, `neverUsed` lists actions no holder used,\nwhich are candidates for a narrower role. A role's actions are the known actions its allow statements (own,\nattached, and inherited) can match; resources and conditions are not considered.\n\nCheck `complete` before acting: it is `false` until usage has been recorded for the whole window, and until then\n\"unused\" only means \"not used since tracking started\". `tracking` is `false` when the `accessUsage` option is off.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#rightsize"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "unusedDays": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/RightSizeResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/suggest": {
   "post": {
    "operationId": "roleMining.suggest",
    "tags": [
     "roleMining"
    ],
    "summary": "Lists ways to simplify how the tenant grants access, most actionable first.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `INVALID_INPUT` for an unknown kind in `kinds`, or `minIdentities` (2 to 10 000), `minRoles` (2 to\n  50), or `limit` (1 to 500) out of range.\n\nThere are four kinds of suggestion:\n\n- **`redundant-binding`**: direct bindings that a permanent group membership already covers, with the same role,\n  the same grant authority, and a group binding that lasts at least as long. Removing them changes nothing today.\n- **`group-binding`**: a role that every member of a group (all active, all with permanent memberships) holds\n  through their own direct binding. Bind it to the group once, so joiners get it and leavers lose it.\n- **`duplicate-roles`**: roles whose statements are identical.\n- **`bundle`**: role combinations many people hold together. Grant them as one\n  [access package](/docs/guides/privileged-access/access-packages) instead of binding each role separately.\n\n`minIdentities` (default 3) is how many people must share a pattern, and `minRoles` (default 2) is the smallest\ncombination reported as a bundle. `limit` (default 50) caps the list, while `summary` counts every suggestion by\nkind. Each suggestion names the roles, people, and group involved, the bindings it would remove, a `savings`\nestimate, and whether `apply` can carry it out.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#suggest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "minIdentities": {
          "type": "number"
         },
         "minRoles": {
          "type": "number"
         },
         "kinds": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/RoleSuggestionKind"
          }
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/RoleMiningResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roleMining/usage": {
   "post": {
    "operationId": "roleMining.usage",
    "tags": [
     "roleMining"
    ],
    "summary": "Returns the recorded access usage of the tenant: per person and action, when it was first and last allowed and how often.",
    "description": "- **Permission:** `iam:analysis:read` on `iam/analysis/*`.\n- **Audited as:** `iam:analysis:read`.\n- **Errors:** `INVALID_INPUT` when `limit` (1 to 1000) or `offset` is out of range.\n\nRecords are sorted by last use, newest first; pass `identityId` to see one person's history. Buffered usage is\nwritten before the read, so the result is current. `tracking` tells you whether the deployment records usage at\nall, and `trackingSince` when this tenant's first use was recorded. Counts are approximate when several server\ninstances record at once.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/role-mining#usage"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         },
         "offset": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tracking": {
             "type": "boolean"
            },
            "trackingSince": {
             "type": "number"
            },
            "total": {
             "type": "number"
            },
            "records": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "identityId": {
                "type": "string"
               },
               "action": {
                "type": "string"
               },
               "firstUsedAt": {
                "type": "number"
               },
               "lastUsedAt": {
                "type": "number"
               },
               "count": {
                "type": "number"
               }
              },
              "required": [
               "identityId",
               "action",
               "firstUsedAt",
               "lastUsedAt",
               "count"
              ],
              "additionalProperties": false
             }
            }
           },
           "required": [
            "tracking",
            "trackingSince",
            "total",
            "records"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/assume": {
   "post": {
    "operationId": "roles.assume",
    "tags": [
     "roles"
    ],
    "summary": "Exchanges your session for a short-lived role session in a target tenant, through a trust the platform root created.",
    "description": "- **Permission:** `iam:roles:assume` on the target role (`iam/{roleId}`), evaluated in your own tenant, and a trust\n  that names you as its source identity.\n- **Audited as:** `iam:roles:assume` in your own tenant, and `role:assumed` in the target tenant (with the trust,\n  your tenant, the duration, the credential format, and the names of any session tags), so both sides can see it.\n- **Errors:** `ROLE_CHAINING_DISABLED` when you call it from a role session; `IMPERSONATION_RESTRICTED` from a \"view\n  as\" session; `NOT_FOUND` when the trust is not in the target tenant; `ACCESS_DENIED` when the trust is revoked,\n  names another source identity, requires MFA your session lacks, expects an external ID you did not match, or does\n  not admit the session tags or source identity you passed; `TENANT_INACTIVE` when the target tenant or an ancestor\n  is not active; `INVALID_INPUT` for a `durationSeconds` outside the allowed range or a malformed session name,\n  source identity, tag, or audience; `FEATURE_DISABLED` for `format: 'jwt'` when the deployment has no `sts.jwt`\n  signing keys; `INVALID_POLICY` or `INVALID_ACTION` for an invalid session `policy`.\n\nUse it for cross-tenant support or automation, or for one task that needs a role nobody should hold permanently.\nThe platform root sets up each trust with [`trust.create`](/docs/reference/api/trust#create): exactly one source\nidentity, one target role, and optionally MFA, an external ID, and a ceiling. The returned `token` is a credential\nfor the target tenant whose permissions are exactly the role's, bounded by the trust's ceiling and by the optional\nsession `policy`, which can only narrow them. Your own roles do not carry over, and neither do the limits of the\ncredential you called with: an API key's scopes or a session token's `policy` only decide whether you may assume the\nrole (checked again on every use), not what the role session may do. Your MFA state and sign-in time do carry over,\nand the address you called from is recorded so the target tenant's IP allowlist and network blocks apply to the\nrole session too.\n\nThe session lasts `durationSeconds`: 900 by default, at most the smaller of the deployment's\n`sts.maxRoleSessionSeconds` (3600 unless configured) and the trust's own limit, and never longer than the session\nyou called from. A value outside that range is refused rather than shortened. A role session cannot assume another\nrole.\n\nOptional inputs describe the session for policies and the audit log: `sessionName` (who or what is acting, such as\na ticket number), `sourceIdentity` (the person behind an automated caller), and `tags` (key/value pairs that\npolicies read as `principal.sessionTags.{key}`). A trust admits no tags and forbids a source identity unless it is\nconfigured to allow them, because both can satisfy policy conditions. `format: 'jwt'` issues the credential as a\nsigned JWT for `audience` instead of an opaque token.\n\n```ts\nconst { token, session } = await iam.api.roles.assume(credential, {\n  tenantId: customerTenantId,\n  trustId,\n  durationSeconds: 900,\n  policy: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['documents:read'], resources: ['*'] }],\n  },\n});\n// Call the API as { token } in customerTenantId until session.expiresAt.\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#assume"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AssumeRoleInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/RoleCredential"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/create": {
   "post": {
    "operationId": "roles.create",
    "tags": [
     "roles"
    ],
    "summary": "Creates a role from a permissions list, an inline policy document, attached policies, inherited roles, or a mix of them.",
    "description": "- **Permission:** `iam:roles:create` on the tenant, plus an active grant authority.\n- **Audited as:** `iam:roles:create`.\n- **Errors:** `INVALID_INPUT` when both `permissions` and `document` are given, `permissions` is empty, more than 20\n  roles are inherited, or the name or description (at most 512 characters) is invalid; `INVALID_POLICY`,\n  `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE` when the permissions or document do not validate against the\n  catalog; `NOT_FOUND` when an attached policy or inherited role is not in this tenant; `PROTECTED_RESOURCE` when\n  inheriting a protected role; `GRANT_AUTHORITY_REQUIRED` when you hold no active grant authority;\n  `LIMIT_EXCEEDED` when the tenant's plan limit for roles is reached.\n\nA new role grants nothing until it is bound. Create one role per job function in your product rather than one per\nperson.\n\n```ts\nconst editor = await iam.api.roles.create(credential, {\n  tenantId,\n  name: 'Editor',\n  description: 'Reads and writes documents',\n  permissions: ['documents:read', 'documents:write'],\n});\n\n// A manager does everything an editor does, plus exports.\nconst manager = await iam.api.roles.create(credential, {\n  tenantId,\n  name: 'Manager',\n  permissions: ['reports:export'],\n  inherits: [editor.id],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RoleInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Role"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/delete": {
   "post": {
    "operationId": "roles.delete",
    "tags": [
     "roles"
    ],
    "summary": "Deletes a role together with its bindings and their activations.",
    "description": "- **Permission:** `iam:roles:delete` on the role, and the grant authority the role was created under (or root).\n- **Audited as:** `iam:roles:delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) while another role inherits it or an\n  [access package](/docs/guides/privileged-access/access-packages) includes it; `PROTECTED_RESOURCE` for a\n  protected role; `ACCESS_DENIED` when another administrator's authority created the role; `NOT_FOUND`;\n  `INVARIANT_VIOLATION`.\n\nEveryone who held the role loses it at once. The in-use checks exist so that deleting a role never silently\nchanges what another role or a package grants: change those first.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "roleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/get": {
   "post": {
    "operationId": "roles.get",
    "tags": [
     "roles"
    ],
    "summary": "Returns one role by id, with its attached policies, inline document, and inherited roles.",
    "description": "- **Permission:** `iam:roles:read` on the role.\n- **Audited as:** `iam:roles:read`.\n- **Errors:** `NOT_FOUND` when the role is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "roleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Role"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/list": {
   "post": {
    "operationId": "roles.list",
    "tags": [
     "roles"
    ],
    "summary": "Lists every role in the tenant, including the protected Owner role.",
    "description": "- **Permission:** `iam:roles:read` on the tenant.\n- **Audited as:** `iam:roles:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Role"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/listBindings": {
   "post": {
    "operationId": "roles.listBindings",
    "tags": [
     "roles"
    ],
    "summary": "Lists who holds a role: its bindings, each with a summary of the person, service account, or group it names.",
    "description": "- **Permission:** `iam:bindings:read` on the role.\n- **Audited as:** `iam:bindings:read`.\n- **Errors:** `NOT_FOUND` when the role is not in this tenant.\n\nExpired bindings are left out. Future-dated and eligible bindings are included, so check `startsAt` and `eligible`\nto tell who holds the role right now. Each entry has `subject` (id, name, email, and kind for an identity; id and\nname for a group). Group bindings are listed as the group, not expanded to members, and holders of roles that\ninherit this one are not included.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#listbindings"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "roleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "$ref": "#/components/schemas/Binding"
             },
             {
              "type": "object",
              "properties": {
               "subject": {
                "$ref": "#/components/schemas/BindingSubject"
               }
              },
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/listSessions": {
   "post": {
    "operationId": "roles.listSessions",
    "tags": [
     "roles"
    ],
    "summary": "Lists the live role sessions in the tenant, of one role or trust when given, newest first.",
    "description": "- **Permission:** `iam:trust:read` on the role (`iam/{roleId}`), or on the tenant without `roleId`.\n- **Audited as:** `iam:trust:read`.\n- **Errors:** `NOT_FOUND` when `roleId` is not a role of this tenant; `INVALID_INPUT` for a `limit` outside 1 to 500.\n\nEach entry is a summary built for the target tenant's administrators: the session id, role, trust, the identity it\nacts as, the source tenant, session name, source identity, the web identity's provider and subject, MFA, format,\ncreation and expiry times, and the recorded client address. Tokens, hashes, policies, and authority ids are never\nincluded. Expired sessions, sessions under a revoked trust, and sessions already below a revocation watermark are\nleft out. `limit` defaults to 100. Use it to see who is currently working in the tenant through a trust before\nrevoking with [`revokeSessions`](#revokesessions).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#listsessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         },
         "trustId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/RoleSessionSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/revokeSessions": {
   "post": {
    "operationId": "roles.revokeSessions",
    "tags": [
     "roles"
    ],
    "summary": "Ends every role session of a role issued before a point in time, whichever trust or provider issued it.",
    "description": "- **Permission:** `iam:roles:revoke-sessions` on the role, with recent authentication.\n- **Audited as:** `iam:roles:revoke-sessions` and `role:sessions-revoked` (with the watermark and the number of\n  sessions deleted).\n- **Errors:** `INVALID_INPUT` when `before` is not a whole number of milliseconds, is negative, or lies in the future;\n  `NOT_FOUND` when the role is not in this tenant; `RECENT_AUTH_REQUIRED`; `ACCESS_DENIED`.\n\nThis is the \"revoke older sessions\" lever for an incident: `before` defaults to now, which ends every role session\nissued so far, while new assumptions keep working. The role's `sessionsRevokedBefore` watermark only moves forward\nand is kept by [`update`](#update) and configuration sync, so a session created before it is refused at its next use\n(with `UNAUTHENTICATED`), and matching rows are deleted at once. It only removes access, so it can be delegated to\nthe target tenant's administrators. Session JWTs that other services verify offline stay valid there until they\nexpire. To end the sessions of one trust or one OIDC provider instead, use\n[`trust.revokeSessions`](/docs/reference/api/trust#revokesessions) or\n[`oidcProviders.revokeSessions`](/docs/reference/api/oidc-providers#revokesessions).\n\n```ts\nconst { revoked, sessionsRevokedBefore } = await iam.api.roles.revokeSessions(credential, { tenantId, roleId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#revokesessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "roleId": {
          "type": "string"
         },
         "before": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "roleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "roleId": {
             "type": "string"
            },
            "sessionsRevokedBefore": {
             "type": "number"
            },
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "roleId",
            "sessionsRevokedBefore",
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/roles/update": {
   "post": {
    "operationId": "roles.update",
    "tags": [
     "roles"
    ],
    "summary": "Changes a role's name, description, permissions or inline document, attached policies, or inherited roles.",
    "description": "- **Permission:** `iam:roles:update` on the role, and the grant authority the role was created under (or root).\n- **Audited as:** `iam:roles:update`.\n- **Errors:** `PROTECTED_RESOURCE` for a protected role, or when inheriting one; `ACCESS_DENIED` when another\n  administrator's authority created the role; `GRANT_AUTHORITY_REQUIRED` when you hold no active grant authority;\n  `INVALID_INPUT` when both `permissions` and `document` are given or the inheritance would form a cycle;\n  `INVALID_POLICY`, `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE`; `NOT_FOUND`; `INVARIANT_VIOLATION` when the change\n  would newly break an enforced [access invariant](/docs/reference/api/invariants).\n\nOnly the fields you pass change. `permissions` replaces the inline document with a new permissions statement,\n`document: null` removes the inline document, `policyIds` replaces the attached set, and `inherits: []` clears\ninheritance. Everyone who holds the role, directly, through a group, or through a role that inherits it, sees the\nchange at their next request, so preview it first with [`impact.preview`](/docs/reference/api/impact#preview).\n\n```ts\nawait iam.api.roles.update(credential, {\n  tenantId,\n  roleId: editor.id,\n  permissions: ['documents:read', 'documents:write', 'documents:share'],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/roles#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/RoleUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Role"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/root/listAdministrators": {
   "post": {
    "operationId": "root.listAdministrators",
    "tags": [
     "root"
    ],
    "summary": "Lists the identities that hold the root capability.",
    "description": "- **Permission:** `iam:identities:read` on the root tenant; root administrators only.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `ACCESS_DENIED` for anyone but a root administrator in an MFA session; `INVALID_INPUT` when\n  `tenantId` is not the root tenant.\n\nUse it to review who holds platform-wide authority, for example in a quarterly access review. Identities are\nreturned without credential material.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/root#listadministrators"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicIdentity"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/root/setAdministrator": {
   "post": {
    "operationId": "root.setAdministrator",
    "tags": [
     "root"
    ],
    "summary": "Grants the root capability to a person in the root tenant, or removes it.",
    "description": "- **Permission:** `iam:root:grant` on the identity; root administrators only, with recent authentication.\n- **Audited as:** `iam:root:grant`.\n- **Errors:** `ACCESS_DENIED` for anyone but a root administrator in an MFA session; `RECENT_AUTH_REQUIRED` without\n  recent authentication; `INVALID_INPUT` when `tenantId` is not the root tenant, `enabled` is not a boolean, or the\n  identity is a service account; `NOT_FOUND` when the identity is not in the root tenant; `LAST_ROOT_ADMIN` when\n  removing the last active root administrator; `LAST_OWNER` when removing the capability from someone who is also\n  the root tenant's last active owner; `INVARIANT_VIOLATION` when the change would newly break an enforced access invariant.\n\nOnly human identities (`kind: 'user'`) of the root tenant qualify. Every session the identity holds, including role sessions it assumed, is revoked in the same transaction, whether\nthe capability is granted or removed. The person signs in again (with MFA) and gets a session that reflects the\nchange, so no session keeps authority it was not issued with.\n\n```ts\nawait iam.api.root.setAdministrator(rootCredential, { tenantId: rootTenantId, identityId, enabled: true });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/root#setadministrator"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "enabled": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "enabled"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/security/blockNetwork": {
   "post": {
    "operationId": "security.blockNetwork",
    "tags": [
     "security"
    ],
    "summary": "Blocks an IP address or CIDR range for the tenant, or for the whole platform, optionally for a limited time.",
    "description": "- **Permission:** `iam:security:manage` on `iam/security/networks`, with recent authentication. A `platform` block\n  also requires a root administrator acting on the root tenant.\n- **Audited as:** `iam:security:manage` and `security:network-block` (metadata: `network`, `reason`, `platform`,\n  `expiresAt`, and `renewed`).\n- **Errors:** `INVALID_INPUT` when `network` is not an IPv4 or IPv6 address or CIDR block, `reason` is empty, the\n  duration is out of range, or the network includes your own address; `ACCESS_DENIED` for a platform block from\n  anyone but a root administrator on the root tenant; `RECENT_AUTH_REQUIRED` without recent authentication;\n  `IMPERSONATION_RESTRICTED` from a \"view as\" session.\n\nA block that would cover your own address, either the one your session was issued from or the one this request\ncomes from, is refused, so you cannot lock yourself out of the session you need to lift it. Blocking a network that\nis already blocked in the same scope renews that block: the reason, the expiry, and who set it are replaced, and\nthe audit metadata says `renewed: true`.\n\n```ts\nawait iam.api.security.blockNetwork(credential, {\n  tenantId,\n  network: '203.0.113.0/24',\n  reason: 'Credential stuffing, incident 4211',\n  durationMs: 24 * 60 * 60 * 1000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/security#blocknetwork"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "network": {
          "type": "string"
         },
         "reason": {
          "type": "string"
         },
         "durationMs": {
          "type": "number"
         },
         "platform": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "network",
         "reason"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "active": {
             "type": "boolean"
            },
            "network": {
             "type": "string"
            },
            "reason": {
             "type": "string"
            },
            "createdAt": {
             "type": "number"
            },
            "createdBy": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            },
            "platform": {
             "type": "boolean"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "active",
            "network",
            "reason",
            "createdAt",
            "createdBy",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/security/listBlocks": {
   "post": {
    "operationId": "security.listBlocks",
    "tags": [
     "security"
    ],
    "summary": "Lists the tenant's network blocks, newest first, each with active telling whether it still applies.",
    "description": "- **Permission:** `iam:security:read` on `iam/security/networks`.\n- **Audited as:** `iam:security:read`.\n\nLapsed blocks stay in the list with `active: false` until the retention worker deletes them. Platform blocks are\nlisted on the root tenant, where they were set.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/security#listblocks"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "active": {
              "type": "boolean"
             },
             "network": {
              "type": "string"
             },
             "reason": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "createdBy": {
              "type": "string"
             },
             "expiresAt": {
              "type": "number"
             },
             "platform": {
              "type": "boolean"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             },
             "uniqueKey": {
              "type": "string"
             }
            },
            "required": [
             "active",
             "network",
             "reason",
             "createdAt",
             "createdBy",
             "id",
             "tenantId"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/security/unblockNetwork": {
   "post": {
    "operationId": "security.unblockNetwork",
    "tags": [
     "security"
    ],
    "summary": "Lifts a network block before it lapses.",
    "description": "- **Permission:** `iam:security:manage` on `iam/security/networks`, with recent authentication. Lifting a platform\n  block also requires a root administrator acting on the root tenant.\n- **Audited as:** `iam:security:manage` and `security:network-unblock` (metadata: `network`, `platform`).\n- **Errors:** `NOT_FOUND` when the block is not in this tenant; `ACCESS_DENIED` for a platform block from anyone but\n  a root administrator; `RECENT_AUTH_REQUIRED` without recent authentication.\n\nThe block is deleted, so traffic from the network is accepted again right away (within seconds on other server\nprocesses).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/security#unblocknetwork"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "blockId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "blockId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "success": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "success"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/create": {
   "post": {
    "operationId": "serviceAccounts.create",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Creates a service account in the tenant, optionally with a date after which it is deactivated.",
    "description": "- **Permission:** `iam:identities:create` on the tenant, and the caller must hold an active grant authority.\n- **Audited as:** `iam:identities:create`.\n- **Errors:** `GRANT_AUTHORITY_REQUIRED` when the caller holds no grant authority; `LIMIT_EXCEEDED` at the tenant's\n  service account limit; `INVALID_INPUT` for an empty name, a description over 512 characters, or an `expiresAt` that\n  is not in the future or is more than ten years away.\n\nThe new account is active but holds no access until you bind roles to it or add it to groups. Automatic\n[access package](/docs/guides/privileged-access/access-packages) rules are evaluated for it right after creation, so\na rule that matches service accounts grants its package at once. Then issue a key with\n[`credentials.create`](/docs/reference/api/credentials#create).\n\n```ts\nconst account = await iam.api.serviceAccounts.create(credential, {\n  tenantId,\n  name: 'Billing sync',\n  description: 'Nightly export to the finance system',\n  expiresAt: Date.parse('2027-06-30T00:00:00Z'),\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Identity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/delete": {
   "post": {
    "operationId": "serviceAccounts.delete",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Deletes a service account, revoking its keys and removing every grant it held.",
    "description": "- **Permission:** `iam:identities:delete` on the account, with recent authentication.\n- **Audited as:** `iam:identities:delete`, plus `identity:delete` with `metadata.kind` set to `service`.\n- **Errors:** `NOT_FOUND` when the id is not a service account of this tenant; `CONFLICT` when it is already deleted;\n  `INVALID_INPUT` when an account tries to delete itself; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nIn one transaction the account's keys and assumed-role sessions end, its role bindings, group memberships,\nactivations, package assignments, relationships, and boundary are removed, pending access requests are cancelled, and\nany grant authority it held is revoked. The account stays as a deleted record, visible with `list({ includeDeleted })`,\nso the audit log keeps resolving its id.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/get": {
   "post": {
    "operationId": "serviceAccounts.get",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Returns one service account by id.",
    "description": "- **Permission:** `iam:identities:read` on the account.\n- **Audited as:** `iam:identities:read`.\n- **Errors:** `NOT_FOUND` when the id is not a service account of this tenant (people are not returned here).\n\nA deleted account is still returned, with `status: 'deleted'`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/list": {
   "post": {
    "operationId": "serviceAccounts.list",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Lists the tenant's service accounts, without deleted ones unless you ask for them.",
    "description": "- **Permission:** `iam:identities:read` on the tenant.\n- **Audited as:** `iam:identities:read`.\n\nPass `includeDeleted: true` to include deleted accounts, for example when resolving old audit entries. Combine it with\n[`credentials.list`](/docs/reference/api/credentials#list) to review which accounts hold keys and when they were\nlast used.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "includeDeleted": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicIdentity"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/setStatus": {
   "post": {
    "operationId": "serviceAccounts.setStatus",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Disables a service account, ending all its access at once, or enables it again.",
    "description": "- **Permission:** `iam:identities:update` on the account, with recent authentication.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `INVALID_TRANSITION` (409) when enabling an account whose `expiresAt` has passed; `NOT_FOUND` when the\n  id is not a service account of this tenant or was deleted; `INVALID_INPUT` for a status other than `active` or\n  `disabled`; `INVARIANT_VIOLATION` when an enforced\n  [access invariant](/docs/guides/governance/change-safety) would newly fail; `RECENT_AUTH_REQUIRED`;\n  `IMPERSONATION_RESTRICTED`.\n\nDisabling deletes every API key the account holds and every role session it assumed, while its bindings and\nmemberships stay in place. Use it to contain a leaked key or pause an integration. Enabling the account again does not\nbring the keys back: issue new ones. To re-enable an expired account, first extend or clear `expiresAt` with\n[`update`](#update), so nobody quietly turns a finished integration back on.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#setstatus"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "active",
           "disabled"
          ]
         }
        },
        "required": [
         "tenantId",
         "identityId",
         "status"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/serviceAccounts/update": {
   "post": {
    "operationId": "serviceAccounts.update",
    "tags": [
     "serviceAccounts"
    ],
    "summary": "Renames a service account, changes its description or directory attributes, or schedules or clears its deactivation.",
    "description": "- **Permission:** `iam:identities:update` on the account.\n- **Audited as:** `iam:identities:update`.\n- **Errors:** `INVALID_INPUT` when nothing is given to change, an attribute is not declared in\n  `permissions.identityAttributes` or has the wrong type, or `expiresAt` is invalid; `NOT_FOUND` when the id is not a\n  service account of this tenant or was deleted; `INVARIANT_VIOLATION` when an enforced access invariant would newly\n  fail.\n\n`attributes` replaces the account's whole attribute set; policies read them as `principal.{name}`. Pass\n`expiresAt: null` to make the account permanent. Automatic access package rules are re-evaluated for the account\nafterwards, so an attribute change can add or remove package access.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/service-accounts#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "attributes": {
          "type": "object",
          "properties": {},
          "additionalProperties": {
           "description": "Any JSON value"
          }
         },
         "expiresAt": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sod/create": {
   "post": {
    "operationId": "sod.create",
    "tags": [
     "sod"
    ],
    "summary": "Declares 2 to 20 roles that nobody may hold together, and reports how many people already hold two of them.",
    "description": "- **Permission:** `iam:sod:manage` on `iam/sod/*`.\n- **Audited as:** `iam:sod:manage`.\n- **Errors:** `INVALID_INPUT` when `name` or `roleIds` is missing, fewer than 2 or more than 20 distinct roles are\n  named, one of them is a protected Owner role, the `mode` is not `prevent` or `detect`, or the name (200\n  characters) or description (1000) is too long; `NOT_FOUND` when a role is not in this tenant.\n\nThe result is the stored rule plus `existingViolations`, the number of conflicts that already exist. Creating a\nrule never fails because of them. To measure a rule's impact before enforcing it, create it with `mode: 'detect'`\nand switch to `prevent` with `update` later.\n\n```ts\nconst rule = await iam.api.sod.create(credential, {\n  tenantId,\n  name: 'Supplier creation vs payment approval',\n  roleIds: [supplierAdmin.id, paymentApprover.id],\n  description: 'Finance controls policy, section 4.2',\n});\n// rule.existingViolations: people who already hold both roles\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sod#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "roleIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "mode": {
          "type": "string",
          "enum": [
           "prevent",
           "detect"
          ]
         },
         "description": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name",
         "roleIds"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "existingViolations": {
             "type": "number"
            },
            "name": {
             "type": "string"
            },
            "description": {
             "type": "string"
            },
            "roleIds": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "mode": {
             "type": "string",
             "enum": [
              "prevent",
              "detect"
             ]
            },
            "createdAt": {
             "type": "number"
            },
            "createdBy": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "existingViolations",
            "name",
            "roleIds",
            "mode",
            "createdAt",
            "createdBy",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sod/delete": {
   "post": {
    "operationId": "sod.delete",
    "tags": [
     "sod"
    ],
    "summary": "Removes a rule, so the combination is no longer checked or reported.",
    "description": "- **Permission:** `iam:sod:manage` on `iam/sod/{ruleId}`.\n- **Audited as:** `iam:sod:manage`.\n- **Errors:** `NOT_FOUND` when the rule is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sod#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "ruleId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "ruleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sod/list": {
   "post": {
    "operationId": "sod.list",
    "tags": [
     "sod"
    ],
    "summary": "Lists the tenant's rules, newest first.",
    "description": "- **Permission:** `iam:sod:read` on `iam/sod/*`.\n- **Audited as:** `iam:sod:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sod#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/SodRule"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sod/update": {
   "post": {
    "operationId": "sod.update",
    "tags": [
     "sod"
    ],
    "summary": "Changes a rule's name, description, roles, or mode.",
    "description": "- **Permission:** `iam:sod:manage` on `iam/sod/{ruleId}`.\n- **Audited as:** `iam:sod:manage`.\n- **Errors:** `NOT_FOUND` when the rule is not in this tenant; `INVALID_INPUT` under the same rules as `create`.\n\nOnly the fields you pass change; `roleIds` replaces the whole list. Switching to `prevent` takes effect for the next\ngranting operation, and conflicts that exist at that moment still do not block unrelated work.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sod#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "ruleId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         },
         "roleIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "mode": {
          "type": "string",
          "enum": [
           "prevent",
           "detect"
          ]
         }
        },
        "required": [
         "tenantId",
         "ruleId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SodRule"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sod/violations": {
   "post": {
    "operationId": "sod.violations",
    "tags": [
     "sod"
    ],
    "summary": "Lists everyone who currently holds two or more roles of a rule, with names for review screens.",
    "description": "- **Permission:** `iam:sod:read` on `iam/sod/*`.\n- **Audited as:** `iam:sod:read`.\n\nBoth `prevent` and `detect` rules are covered; pass `ruleId` to check one rule (an unknown id returns an empty\nlist). Each entry names the rule (`ruleId`, `ruleName`, `mode`), the person (`identityId`, and `identityName`, their\nemail or else their name), and the conflicting roles (`roleIds`, `roleNames`). Disabled identities are included,\nbecause they can be re-enabled. To fix a violation, remove one of the conflicting grants: delete a binding with\n[`bindings.delete`](/docs/reference/api/bindings#delete), remove the person from the group that carries the role,\nor revoke the access package that granted it.\n\n```ts\nconst violations = await iam.api.sod.violations(credential, { tenantId, ruleId: rule.id });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sod#violations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "ruleId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "identityName": {
              "type": "string"
             },
             "roleNames": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "ruleId": {
              "type": "string"
             },
             "ruleName": {
              "type": "string"
             },
             "mode": {
              "type": "string",
              "enum": [
               "prevent",
               "detect"
              ]
             },
             "identityId": {
              "type": "string"
             },
             "roleIds": {
              "type": "array",
              "items": {
               "type": "string"
              }
             }
            },
            "required": [
             "identityName",
             "roleNames",
             "ruleId",
             "ruleName",
             "mode",
             "identityId",
             "roleIds"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sts/assumeRoleWithWebIdentity": {
   "post": {
    "operationId": "sts.assumeRoleWithWebIdentity",
    "tags": [
     "sts"
    ],
    "summary": "Exchanges a verified external OpenID Connect token, such as a GitHub Actions or Kubernetes token, for a role session under a web-identity trust.",
    "description": "- **Permission:** None: public. The external token is the only credential, and the trust's claim conditions decide\n  whether it is admitted.\n- **Audited as:** `role:assumed-with-web-identity` in the trust's tenant (outcome `allow`, or `deny` with a `reason`\n  once the trust has resolved).\n- **Errors:** `WEB_IDENTITY_REJECTED` (403) for every refusal that depends on stored state or on the token (unknown\n  or revoked trust, disabled provider, bad signature, issuer, audience or lifetime, unmet conditions, replay, inactive\n  service account, and so on), always with the same body; `FEATURE_DISABLED` when `sts.webIdentity.enabled` is off,\n  or for `format: 'jwt'` without `sts.jwt`; `INVALID_INPUT` for a malformed request, `durationSeconds`, or\n  `audience`; `RATE_LIMITED` once the trust's exchange budget (`sts.webIdentity.maxExchangesPerWindow`) is spent;\n  `LIMIT_EXCEEDED` (409) when the trust already holds `sts.webIdentity.maxSessionsPerTrust` live sessions;\n  `IP_BLOCKED` or `IP_NOT_ALLOWED` from the tenant's network rules; `ACCESS_DENIED` for a JWT audience the service\n  account may not obtain.\n\nThe session acts as the trust's service account in the trust's tenant, with the role's permissions bounded by the\ntrust's ceiling, the optional scope-down `policy`, and the grant authorities of whoever created the trust and the\nprovider. It is kind `role` with `session.webIdentity` set, never carries MFA, and ends when the provider is disabled,\nthe trust is revoked, the service account is disabled, or either authority is revoked. `sessionName` is required and\nreaches policies as `principal.sessionName`; the verified subject reaches them as `principal.webIdentitySubject`.\n\nBecause callers cannot tell why a token was refused, administrators debug with\n[`trust.evaluateWebIdentity`](/docs/reference/api/trust#evaluatewebidentity), which reports the reason and every\nfailing condition. Each token can be redeemed once per provider unless the provider sets `replayProtection: 'off'`.\n\n```bash\n# In a GitHub Actions job with `permissions: id-token: write`.\nID_TOKEN=$(curl -sH \"Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN\" \\\n  \"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://iam.example.com\" | jq -r .value)\ncurl -s https://iam.example.com/api/iam/sts/assumeRoleWithWebIdentity \\\n  -H 'Content-Type: application/json' -H 'X-Better-IAM: 1' \\\n  -d \"{\\\"tenantId\\\":\\\"$TENANT_ID\\\",\\\"trustId\\\":\\\"$TRUST_ID\\\",\\\"webIdentityToken\\\":\\\"$ID_TOKEN\\\",\\\"sessionName\\\":\\\"deploy-$GITHUB_RUN_ID\\\"}\"\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sts#assumerolewithwebidentity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/WebIdentityExchangeInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/WebIdentityCredential"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sts/getCallerIdentity": {
   "post": {
    "operationId": "sts.getCallerIdentity",
    "tags": [
     "sts"
    ],
    "summary": "Returns who the presented credential acts as: identity, tenant, session kind and id, format, MFA, expiry, and any role, trust, session name, tags, or web identity behind it.",
    "description": "- **Permission:** None beyond a valid credential. It works for every session kind.\n- **Audited as:** nothing; it records no event.\n- **Errors:** `UNAUTHENTICATED` when the credential is invalid, expired, or revoked.\n\nThe credential is re-validated exactly as for any other call, so this doubles as the online revocation check for a\nservice that holds a session JWT: a token that still verifies offline but was revoked in IAM fails here. The result\nis an allowlist projection and never includes hashes, policies, the source session, or authority ids. The CLI's\n`whoami` command prints it.\n\n```ts\nconst caller = await iam.api.sts.getCallerIdentity({ token });\nif (caller.sessionKind === 'role') console.log(`acting as role ${caller.roleId} via trust ${caller.trustId}`);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sts#getcalleridentity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CallerIdentity"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sts/getSessionToken": {
   "post": {
    "operationId": "sts.getSessionToken",
    "tags": [
     "sts"
    ],
    "summary": "Mints a short-lived session token for your own identity from a signed-in session or an API key, optionally narrowed by a policy and attested with a fresh MFA code.",
    "description": "- **Permission:** `iam:session-tokens:create` on your own identity (`iam/{identityId}`) in your tenant. Owners hold it\n  through the Owner role; anyone else needs an explicit grant.\n- **Audited as:** `iam:session-tokens:create` and `session-token:issued` (with the new session id, duration, format,\n  and whether an MFA code was verified).\n- **Errors:** `CREDENTIAL_CHAINING_DISABLED` (400) from a role session or another session token;\n  `IMPERSONATION_RESTRICTED` from a \"view as\" session; `MFA_NOT_ENROLLED` for `mfaCode` from an API key or a person\n  without an authenticator; `INVALID_MFA` for a wrong or reused code; `RATE_LIMITED` after too many codes;\n  `LIMIT_EXCEEDED` (409) when you already hold `sts.maxSessionTokensPerIdentity` live tokens (50 by default);\n  `INVALID_INPUT` for a duration, session name, format, or audience out of bounds; `FEATURE_DISABLED` for\n  `format: 'jwt'` without `sts.jwt`; `ACCESS_DENIED`.\n\nThe token acts with your own grants, bounded by `policy` and by the source's own limits (an API key's scopes and\nauthority carry over), and lasts `durationSeconds`: 3600 by default, at most `sts.maxSessionTokenSeconds` (12 hours\nunless configured), and never beyond the source's expiry. It ends when its source ends, and using it never refreshes\nthe source's idle timer, so prefer an API key as the source for long-running automation.\n\nWith `mfaCode` (a current authenticator code; people only), the token carries `mfa: true` and a fresh MFA time, so it\ncan then assume roles through trusts that require MFA. This is the MFA-then-assume pattern for command-line tools:\nroles cannot take a code themselves. Without a code, the source's MFA state is copied; API keys never carry MFA.\nThe token's `policy` and its source's scopes decide which roles it may assume, but they do not carry into the role\nsession, which acts with the role's permissions within the trust's ceiling and its own session `policy`.\n\n```ts\n// A CLI step-up: a one-hour, read-only token that can assume an MFA-gated role.\nconst { token, expiresAt } = await iam.api.sts.getSessionToken(\n  { token: signedInToken },\n  {\n    mfaCode: '123456',\n    sessionName: 'deploy-cli',\n    policy: {\n      version: 1,\n      statements: [{ effect: 'allow', actions: ['iam:roles:assume', 'documents:read'], resources: ['*'] }],\n    },\n  },\n);\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/sts#getsessiontoken"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GetSessionTokenInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TemporaryCredential"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/addMember": {
   "post": {
    "operationId": "teams.addMember",
    "tags": [
     "teams"
    ],
    "summary": "Adds a person to a team as a member (default) or maintainer, optionally until expiresAt.",
    "description": "- **Permission:** `iam:teams:update` on `iam/{teamId}`, or maintaining the team or a team above it.\n- **Audited as:** `iam:teams:update` and `team:member:add` (`identityId`, `role`, `source`, `expiresAt`, `via`).\n- **Errors:** `CONFLICT` (409) when the person is already a live member; `INVALID_INPUT` for a service account, agent,\n  or inactive person; `GRANT_AUTHORITY_REQUIRED` / `ACCESS_DENIED` when an administrator lacks authority over what the\n  team holds; `SOD_CONFLICT` (409) when the roles the team brings conflict with the person's; `NOT_FOUND`.\n\nA pending join request of the person is marked approved.\n\n```ts\nawait iam.api.teams.addMember(credential, {\n  tenantId,\n  teamId,\n  identityId,\n  role: 'member',\n  expiresAt: Date.now() + 30 * 86_400_000,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#addmember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "role": {
          "type": "string",
          "enum": [
           "member",
           "maintainer"
          ]
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "teamId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamMember"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/addMembers": {
   "post": {
    "operationId": "teams.addMembers",
    "tags": [
     "teams"
    ],
    "summary": "Adds up to 100 people with the same role and expiry in one transaction; one failure rejects the batch.",
    "description": "- **Permission:** as `addMember`.\n- **Audited as:** `iam:teams:update` and one `team:member:add` per person.\n- **Errors:** as `addMember`; `INVALID_INPUT` for an empty or oversized list.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#addmembers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "identityIds": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "role": {
          "type": "string",
          "enum": [
           "member",
           "maintainer"
          ]
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "teamId",
         "identityIds"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "members": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/TeamMember"
             }
            }
           },
           "required": [
            "members"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/approveRequest": {
   "post": {
    "operationId": "teams.approveRequest",
    "tags": [
     "teams"
    ],
    "summary": "Grants a pending join request: the requester joins as a member (optionally until expiresAt) and is emailed (team-join-decided). Nobody decides their own request.",
    "description": "- **Permission:** `iam:teams:update` on the team, or maintaining it or a team above it.\n- **Audited as:** `iam:teams:update`, `team:member:add` (`source: approve`), and `team:join:approve`.\n- **Errors:** `INVALID_TRANSITION` (409) when the request is no longer pending (decided, withdrawn, or lapsed);\n  `ACCESS_DENIED` for your own request; `NOT_FOUND`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#approverequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "expiresAt": {
          "type": "number"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamJoinRequestView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/cancelRequest": {
   "post": {
    "operationId": "teams.cancelRequest",
    "tags": [
     "teams"
    ],
    "summary": "Withdraws your own pending join request.",
    "description": "- **Permission:** None beyond an ordinary user session of the organization (not while impersonating).\n- **Audited as:** `team:join:cancel`.\n- **Errors:** `INVALID_TRANSITION` when the request is no longer pending; `NOT_FOUND` for someone else's request.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#cancelrequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamJoinRequestView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/cancelReview": {
   "post": {
    "operationId": "teams.cancelReview",
    "tags": [
     "teams"
    ],
    "summary": "Cancels an open membership review without changing the team.",
    "description": "- **Permission:** `iam:teams:update` on the team (administrators; maintainers cannot cancel).\n- **Audited as:** `iam:teams:update` and `team:review:cancel`.\n- **Errors:** `INVALID_TRANSITION` (409) when the review is no longer open; `NOT_FOUND`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#cancelreview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "reviewId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "reviewId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamReviewView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/candidates": {
   "post": {
    "operationId": "teams.candidates",
    "tags": [
     "teams"
    ],
    "summary": "People who could be added: active people of the organization who are not direct members, matched on name or email by query, at most limit (default 50, up to 200).",
    "description": "- **Permission:** `iam:teams:update` on the team, or maintaining it or a team above it.\n- **Audited as:** `iam:teams:update`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#candidates"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "query": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamPerson"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/completeReview": {
   "post": {
    "operationId": "teams.completeReview",
    "tags": [
     "teams"
    ],
    "summary": "Completes an open review: people decided remove leave the team, and people nobody decided on follow the review's onUndecided.",
    "description": "- **Permission:** `iam:teams:update` on the team, or maintaining it (or a team above) once every person is decided.\n- **Audited as:** `iam:teams:update` (maintainers with `via: team-maintainer`), `team:member:remove` (`source: review`)\n  per removal, and `team:review:complete` with the counts.\n- **Errors:** `INVALID_TRANSITION` (409) when the review is no longer open, or when a maintainer completes it with\n  people still undecided; `NOT_FOUND`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#completereview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "reviewId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "reviewId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamReviewView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/create": {
   "post": {
    "operationId": "teams.create",
    "tags": [
     "teams"
    ],
    "summary": "Creates a team with its backing group.",
    "description": "- **Permission:** `iam:teams:create` on the tenant. With `parentId`, also `iam:teams:update` on the parent and\n  authority over what the parent (and the teams above it) hold.\n- **Audited as:** `iam:teams:create`, `team:create`, and `team:member:add` per maintainer.\n- **Errors:** `CONFLICT` (409) when the slug is taken; `INVALID_INPUT` for a bad slug, more than ten levels of nesting,\n  or more than 20 maintainers; `LIMIT_EXCEEDED` past 1000 teams or the tenant's group limit.\n\n```ts\nconst platform = await iam.api.teams.create(credential, {\n  tenantId,\n  name: 'Platform',\n  joinPolicy: 'request',\n  maintainerIds: [leadId],\n});\nawait iam.api.bindings.create(credential, {\n  tenantId,\n  roleId,\n  subjectType: 'group',\n  subjectId: platform.groupId,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TeamInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/decideReview": {
   "post": {
    "operationId": "teams.decideReview",
    "tags": [
     "teams"
    ],
    "summary": "Records keep or remove for up to 200 people under an open review, each with an optional note. A later decision replaces an earlier one; nothing changes in the team until the review completes.",
    "description": "- **Permission:** `iam:teams:update` on the team, or maintaining it (or a team above).\n- **Audited as:** `iam:teams:update` (maintainers with `via: team-maintainer`) and `team:review:decide` with the counts.\n- **Errors:** `ACCESS_DENIED` for a decision on your own membership; `NOT_FOUND` for a person who is not under review;\n  `INVALID_TRANSITION` (409) when the review is no longer open; `INVALID_INPUT` without 1-200 decisions.\n\n```ts\nawait iam.api.teams.decideReview(maintainerSession, {\n  tenantId,\n  reviewId,\n  decisions: [\n    { identityId: aliceId, decision: 'keep' },\n    { identityId: carolId, decision: 'remove', note: 'Moved to Sales' },\n  ],\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#decidereview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "reviewId": {
          "type": "string"
         },
         "decisions": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "identityId": {
             "type": "string"
            },
            "decision": {
             "$ref": "#/components/schemas/TeamReviewDecision"
            },
            "note": {
             "type": "string"
            }
           },
           "required": [
            "identityId",
            "decision"
           ],
           "additionalProperties": false
          }
         }
        },
        "required": [
         "tenantId",
         "reviewId",
         "decisions"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamReviewView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/delete": {
   "post": {
    "operationId": "teams.delete",
    "tags": [
     "teams"
    ],
    "summary": "Deletes a team, its memberships and join requests, and its backing group with the bindings and relationships on it.",
    "description": "- **Permission:** `iam:teams:delete` on the team, and authority over the backing group's bindings.\n- **Audited as:** `iam:teams:delete` and `team:delete`.\n- **Errors:** `RESOURCE_IN_USE` (409) while teams sit below it, while the backing group approves requests for an\n  eligible binding or a package, or while an access package rule names the team (`identity.teams`) or its backing\n  group (`identity.groups`).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean",
             "const": true
            },
            "members": {
             "type": "number"
            }
           },
           "required": [
            "deleted",
            "members"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/denyRequest": {
   "post": {
    "operationId": "teams.denyRequest",
    "tags": [
     "teams"
    ],
    "summary": "Refuses a pending join request; the requester is emailed with the note.",
    "description": "- **Permission:** as `approveRequest`.\n- **Audited as:** `iam:teams:update` and `team:join:deny`.\n- **Errors:** as `approveRequest`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#denyrequest"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "requestId": {
          "type": "string"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "requestId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamJoinRequestView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/get": {
   "post": {
    "operationId": "teams.get",
    "tags": [
     "teams"
    ],
    "summary": "One team with its path (the teams above it), its children, department, maintainers, total member count (with the teams below), and the roles its members hold through it or a team above (inherited).",
    "description": "- **Permission:** `iam:teams:read` on the team, or belonging to it (directly or through a team below it).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/getReview": {
   "post": {
    "operationId": "teams.getReview",
    "tags": [
     "teams"
    ],
    "summary": "One membership review with every person under it: their role, the decision, who made it and when, and the note.",
    "description": "- **Permission:** `iam:teams:read` on the team, or maintaining it (or a team above).\n- **Errors:** `NOT_FOUND` for a review of another team or tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#getreview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "reviewId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "reviewId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamReviewView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/leave": {
   "post": {
    "operationId": "teams.leave",
    "tags": [
     "teams"
    ],
    "summary": "Leaves a team you belong to directly.",
    "description": "- **Permission:** None beyond an ordinary user session of the organization (not while impersonating).\n- **Audited as:** `team:leave`.\n- **Errors:** `NOT_FOUND` when you are not a direct member.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#leave"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "left": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "left"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/list": {
   "post": {
    "operationId": "teams.list",
    "tags": [
     "teams"
    ],
    "summary": "Every team with member, maintainer, and child counts, in name order. Filters: parentId (null for top-level teams), departmentId, and query (name or slug).",
    "description": "- **Permission:** `iam:teams:read` on the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "parentId": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "departmentId": {
          "type": "string"
         },
         "query": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamSummary"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/listForIdentity": {
   "post": {
    "operationId": "teams.listForIdentity",
    "tags": [
     "teams"
    ],
    "summary": "The teams one person belongs to directly, with their role, expiry, and the teams above each one.",
    "description": "- **Permission:** `iam:teams:read` on `iam/{identityId}`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#listforidentity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "allOf": [
             {
              "$ref": "#/components/schemas/TeamRef"
             },
             {
              "type": "object",
              "properties": {
               "description": {
                "type": "string"
               },
               "role": {
                "$ref": "#/components/schemas/TeamRole"
               },
               "expiresAt": {
                "type": "number"
               },
               "parents": {
                "type": "array",
                "items": {
                 "$ref": "#/components/schemas/TeamRef"
                },
                "description": "Teams above it, whose access the membership also brings."
               }
              },
              "required": [
               "role",
               "parents"
              ],
              "additionalProperties": false
             }
            ]
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/listMembers": {
   "post": {
    "operationId": "teams.listMembers",
    "tags": [
     "teams"
    ],
    "summary": "The team's live direct members with their role, expiry, and who added them; includeChildTeams adds the members of every team below, each with the team they belong to.",
    "description": "- **Permission:** `iam:teams:read` on the team, or belonging to it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#listmembers"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "includeChildTeams": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamMemberView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/listMine": {
   "post": {
    "operationId": "teams.listMine",
    "tags": [
     "teams"
    ],
    "summary": "Your teams (with role, expiry, and parents), your join requests (newest first), the teams that take join requests that you are not in, and the open membership reviews of teams you maintain ( eviews,…",
    "description": "- **Permission:** None beyond an ordinary user session of the organization.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#listmine"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/MyTeams"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/listRequests": {
   "post": {
    "operationId": "teams.listRequests",
    "tags": [
     "teams"
    ],
    "summary": "A team's join requests, pending by default (status picks another state; lapsed requests read as expired).",
    "description": "- **Permission:** `iam:teams:read` on the team, or maintaining it or a team above it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#listrequests"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "pending",
           "approved",
           "denied",
           "cancelled",
           "expired"
          ]
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamJoinRequestView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/listReviews": {
   "post": {
    "operationId": "teams.listReviews",
    "tags": [
     "teams"
    ],
    "summary": "Membership reviews, newest first (at most 100), without their items: of one team with teamId, or of every team. status (open, completed, cancelled) filters.",
    "description": "- **Permission:** With `teamId`, `iam:teams:read` on the team or maintaining it (or a team above); without it,\n  `iam:teams:read` on the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#listreviews"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "cancelled",
           "open",
           "completed"
          ]
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamReviewView"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/reconcile": {
   "post": {
    "operationId": "teams.reconcile",
    "tags": [
     "teams"
    ],
    "summary": "Runs team sync for every synced team (synced: team memberships added, removed, and updated), then recomputes every backing group from team membership, after a restore, an import, or a manual repair:…",
    "description": "- **Permission:** `iam:teams:update` on the tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#reconcile"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "allOf": [
            {
             "$ref": "#/components/schemas/TeamSyncResult"
            },
            {
             "type": "object",
             "properties": {
              "synced": {
               "$ref": "#/components/schemas/TeamGroupSyncResult"
              }
             },
             "required": [
              "synced"
             ],
             "additionalProperties": false
            }
           ]
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/removeMember": {
   "post": {
    "operationId": "teams.removeMember",
    "tags": [
     "teams"
    ],
    "summary": "Removes a direct member; the backing groups and the person's activations of their eligible bindings follow.",
    "description": "- **Permission:** as `addMember`.\n- **Audited as:** `iam:teams:update` and `team:member:remove`.\n- **Errors:** `NOT_FOUND` when the person is not a live direct member; `INVALID_TRANSITION` (409) for a member\n  [team sync](#team-sync) manages.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#removemember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean",
             "const": true
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/requestToJoin": {
   "post": {
    "operationId": "teams.requestToJoin",
    "tags": [
     "teams"
    ],
    "summary": "Asks to join a team whose joinPolicy is request, with an optional justification.",
    "description": "- **Permission:** None beyond an ordinary user session of the organization (not while impersonating).\n- **Audited as:** `team:join:request`.\n- **Errors:** `INVALID_TRANSITION` (409) when the team does not take requests; `CONFLICT` when you are already a member\n  or already asked.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#requesttojoin"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "justification": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamJoinRequestView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/startReview": {
   "post": {
    "operationId": "teams.startReview",
    "tags": [
     "teams"
    ],
    "summary": "Opens a membership review of the team, due at dueAt (one to 90 days ahead; in 14 days by default), with an optional note for the maintainers.",
    "description": "- **Permission:** `iam:teams:update` on the team (administrators).\n- **Audited as:** `iam:teams:update` and `team:review:start`.\n- **Errors:** `CONFLICT` (409) while another review of the team is open; `INVALID_TRANSITION` (409) when the team has\n  no manual members; `INVALID_INPUT` for a `dueAt` outside one to 90 days.\n\n```ts\nconst review = await iam.api.teams.startReview(credential, {\n  tenantId,\n  teamId,\n  onUndecided: 'remove',\n  note: 'Quarterly access review',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#startreview"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "dueAt": {
          "type": "number"
         },
         "onUndecided": {
          "type": "string",
          "enum": [
           "keep",
           "remove"
          ]
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "teamId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamReviewView"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/suggestBirthright": {
   "post": {
    "operationId": "teams.suggestBirthright",
    "tags": [
     "teams"
    ],
    "summary": "Roles and groups that most of a team's members already hold by hand, proposed as a ready-made automatic access package whose rule names the team.",
    "description": "- **Permission:** `iam:analysis:read` on the tenant.\n- **Audited as:** Not audited; it only reads.\n- **Errors:** `INVALID_INPUT` for `minShare` outside 0.5-1; `NOT_FOUND` for an unknown `teamId`.\n\nA team's people are its members and those of every team below it (who a rule naming it would match). It works like\n[`departments.suggestBirthright`](/docs/reference/api/departments#suggestbirthright): plain grants held by at least\n`minShare` (default 0.8) of at least `minPeople` (default 3) people, never repeating what is suggested for a team above\nor what automatic packages already grant, each with a `package` ready for `packages.create`. A child team whose\nmembers all belong to its parent's suggestion gets none of its own.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#suggestbirthright"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "minShare": {
          "type": "number"
         },
         "minPeople": {
          "type": "number"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/BirthrightSuggestion"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/update": {
   "post": {
    "operationId": "teams.update",
    "tags": [
     "teams"
    ],
    "summary": "Renames, re-slugs, re-describes, moves (parentId, null for top level), or re-files (departmentId, null to clear) a team, or changes joinPolicy, memberManagement, or syncGroupIds (team sync; null stop…",
    "description": "- **Permission:** `iam:teams:update` on the team; moving under a parent also needs `iam:teams:update` on it and\n  authority over what it holds. Maintainers cannot change settings.\n- **Audited as:** `iam:teams:update` and `team:update` (`fields`, and the parents when moved).\n- **Errors:** `INVALID_INPUT` when moving under itself or a team below it, or past ten levels; `CONFLICT` for a taken\n  slug.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TeamUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamDetail"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/teams/updateMember": {
   "post": {
    "operationId": "teams.updateMember",
    "tags": [
     "teams"
    ],
    "summary": "Changes a member's role or expiry (expiresAt: null makes the membership permanent).",
    "description": "- **Permission:** as `addMember`.\n- **Audited as:** `iam:teams:update` and `team:member:update`.\n- **Errors:** `NOT_FOUND` when the person is not a live direct member; `INVALID_TRANSITION` (409) for a new end of a\n  synced membership (it follows the source group).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/teams#updatemember"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "teamId": {
          "type": "string"
         },
         "identityId": {
          "type": "string"
         },
         "role": {
          "type": "string",
          "enum": [
           "member",
           "maintainer"
          ]
         },
         "expiresAt": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "number"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "teamId",
         "identityId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TeamMember"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/acceptInvitation": {
   "post": {
    "operationId": "tenants.acceptInvitation",
    "tags": [
     "tenants"
    ],
    "summary": "Redeems an owner invitation: creates the first owner of a pending tenant, activates the tenant, and signs the owner in.",
    "description": "- **Permission:** None: public. The token from the `owner-invitation` email is the proof.\n- **Audited as:** `tenant:activate`, with the new owner as the actor.\n- **Errors:** `INVITATION_INVALID` when the tenant is not pending, the token is unknown, used, revoked, or expired,\n  or the creator's grant authority has been revoked; `TENANT_INACTIVE` when an ancestor is not active;\n  `INVALID_INPUT` for a missing name; `WEAK_PASSWORD` or `BREACHED_PASSWORD` when the password fails the password\n  rules. With `linkCredential`: `LINKING_DISABLED` unless linked onboarding is on, `RECENT_AUTH_REQUIRED`,\n  `PROTECTED_IDENTITY` for a root administrator, `INVALID_LINK` for anything but an ordinary user session, and\n  `ACCESS_DENIED` unless the credential belongs to the person who created the tenant.\n\nThe owner's email counts as verified. Acceptance creates the protected Owner policy and role, binds the role to the\nowner under the grant authority reserved when the tenant was created, marks the invitation consumed, and sets the\ntenant `active`. The result is the owner's public identity plus either `{ token, session }` or an MFA challenge\nwhen the tenant's policy (for example from `tenantDefaults`) requires MFA. Over HTTP, a response that issues a\nsession also sets the session cookie.\n\nPass `linkCredential`, the creator's own recently authenticated session, to\n[link](/docs/reference/api/links#create) the creator's existing account to the new owner account in the same step\nso they can switch between them. It requires `onboarding: { mode: 'linked' }` in the server options.\n\n```ts\nconst result = await client.tenants.acceptInvitation({\n  tenantId: params.tenant,\n  token: params.token,\n  name: form.name,\n  password: form.password,\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#acceptinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "token": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "password": {
          "type": "string"
         },
         "linkCredential": {
          "$ref": "#/components/schemas/CredentialInput"
         }
        },
        "required": [
         "tenantId",
         "token",
         "name",
         "password"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/EnrollmentResult"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/create": {
   "post": {
    "operationId": "tenants.create",
    "tags": [
     "tenants"
    ],
    "summary": "Creates a child tenant in the pending state under a parent and emails an invitation to its first owner.",
    "description": "- **Permission:** `iam:tenants:create` on the parent tenant, with recent authentication, and an active grant\n  authority in the parent.\n- **Audited as:** `iam:tenants:create`, in the parent tenant's log.\n- **Errors:** `INVALID_HIERARCHY` when `type` is not an allowed child of the parent's type; `MAX_DEPTH` when the\n  parent is already at the deepest level; `DELIVERY_REQUIRED` without an email delivery callback; `SLUG_TAKEN` or\n  `INVALID_INPUT` for a slug in use or badly formed; `GRANT_AUTHORITY_REQUIRED` without an active grant authority,\n  or `ACCESS_DENIED` when `authorityId` is not one of yours; `INVALID_POLICY` or `INVALID_ACTION` for an invalid\n  boundary; `INVALID_INPUT` for a `region` the deployment does not know, or one other than its own when regions\n  keep separate databases; `RECENT_AUTH_REQUIRED`.\n\nThe new tenant receives `tenantDefaults` (limits and authentication policy), the optional `slug`, and an optional\n`boundary`. In a multi-region deployment, `region` sets its home region; without it the tenant inherits its\nparent's, and an organization directly under the (region-less) root is homed in the region that creates it. A grant authority is reserved for the future owner as a child of yours (or of `authorityId`, one of\nyour own authorities in the parent). That keeps the owner within your authority chain: if your authority is\nrevoked, the invitation can no longer be accepted and the owner's grants stop applying. The token goes only into\nthe `owner-invitation` email; the result has the tenant, the invitation ID, and the owner's address.\n\nNobody but a root administrator can act inside the tenant until the owner accepts. If they never do, a root\nadministrator can re-send or revoke the invitation, or delete the tenant.\n\n```ts\nconst { tenant, invitationId } = await iam.api.tenants.create(credential, {\n  parentId: rootTenantId,\n  type: 'organization',\n  name: 'Acme',\n  ownerEmail: 'owner@acme.example',\n  slug: 'acme',\n});\n// tenant.status === 'pending' until the owner accepts\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "parentId": {
          "type": "string"
         },
         "type": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "ownerEmail": {
          "type": "string"
         },
         "slug": {
          "type": "string"
         },
         "boundary": {
          "$ref": "#/components/schemas/PolicyDocument"
         },
         "authorityId": {
          "type": "string"
         },
         "region": {
          "type": "string"
         }
        },
        "required": [
         "parentId",
         "type",
         "name",
         "ownerEmail"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenant": {
             "$ref": "#/components/schemas/Tenant"
            },
            "invitationId": {
             "type": "string"
            },
            "ownerEmail": {
             "type": "string"
            }
           },
           "required": [
            "tenant",
            "invitationId",
            "ownerEmail"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/get": {
   "post": {
    "operationId": "tenants.get",
    "tags": [
     "tenants"
    ],
    "summary": "Returns a tenant with its status, parent, alias, policies, limits, and boundary.",
    "description": "- **Permission:** `iam:tenants:read` on the tenant.\n- **Audited as:** `iam:tenants:read`.\n- **Errors:** `NOT_FOUND` when the tenant does not exist.\n\nRead it before calling a setter, because each setter replaces its whole value.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/listChildren": {
   "post": {
    "operationId": "tenants.listChildren",
    "tags": [
     "tenants"
    ],
    "summary": "Lists the direct children of a tenant, whatever their status.",
    "description": "- **Permission:** `iam:tenants:read` on the tenant.\n- **Audited as:** `iam:tenants:read`.\n\nPending, suspended, and deleted children are included until they are purged. Only direct children are returned;\nwalking further down needs access to each child, which a root administrator has everywhere.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#listchildren"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Tenant"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/listInvitations": {
   "post": {
    "operationId": "tenants.listInvitations",
    "tags": [
     "tenants"
    ],
    "summary": "Lists a tenant's owner invitations without their tokens.",
    "description": "- **Permission:** `iam:tenants:read` on the tenant.\n- **Audited as:** `iam:tenants:read`.\n\nEach invitation shows the invited email, the grant authority reserved for the owner, when it was created and\nexpires, and whether it was `consumed` or `revoked`. While the tenant is pending, only a root administrator can\nread them (see [the tenant tree](#the-tenant-tree)).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#listinvitations"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "email": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "expiresAt": {
              "type": "number"
             },
             "authorityId": {
              "type": "string"
             },
             "consumed": {
              "type": "boolean"
             },
             "revoked": {
              "type": "boolean"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             }
            },
            "required": [
             "email",
             "createdAt",
             "expiresAt",
             "authorityId",
             "consumed",
             "id",
             "tenantId"
            ],
            "additionalProperties": {}
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/lookup": {
   "post": {
    "operationId": "tenants.lookup",
    "tags": [
     "tenants"
    ],
    "summary": "Resolves a tenant's public alias, or an address it signs in at, to its ID, name, and type, so a sign-in page can ask for an organization name instead of a tenant ID.",
    "description": "- **Permission:** None: public.\n- **Errors:** `NOT_FOUND` when no active tenant has that alias or address, including pending, suspended, and\n  deleted tenants and tenants under an inactive ancestor; `WRONG_REGION` (421) when another region serves the\n  organization; `INVALID_INPUT` for a malformed slug.\n\nPass `slug` (matched case-insensitively) or `host`, such as `acme.signin.example.com` or a verified custom hostname.\nWith organization addresses or regions configured, the result also names the organization's home `region` and its\ncanonical `signInUrl`. `WRONG_REGION` carries the region and the sign-in URL there (`location`), so a global sign-in\npage can redirect. `slug` is empty only for an organization without an alias, found by its custom hostname.\n\nIt is not audited. Aliases are discovery data by design, so apply ingress rate limits to this route and never put\nanything secret in an alias.\n\n```ts\ntry {\n  const { tenantId } = await client.tenants.lookup({ slug: 'acme' });\n  await client.auth.signIn({ tenantId, email, password });\n} catch (error) {\n  if (error instanceof IamClientError && error.code === 'WRONG_REGION' && error.location)\n    location.assign(error.location); // the organization signs in in another region\n  else throw error;\n}\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#lookup"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "slug": {
          "type": "string"
         },
         "host": {
          "type": "string"
         }
        },
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/TenantDiscovery"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/reparent": {
   "post": {
    "operationId": "tenants.reparent",
    "tags": [
     "tenants"
    ],
    "summary": "Moves a tenant, with its whole subtree, under a different parent.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant being moved, with recent authentication, plus a grant\n  authority in the new parent (a root administrator always has one).\n- **Audited as:** `iam:tenants:update` and `tenant:reparent` (with the old and new parent).\n- **Errors:** `INVALID_TRANSITION` for the root tenant, a pending tenant, or a deleted one; `INVALID_INPUT` when it\n  is already under that parent; `TENANT_INACTIVE` when the new parent or one of its ancestors is not active;\n  `INVALID_HIERARCHY` when the type is not allowed under the new parent, or the new parent is inside the tenant's\n  own subtree; `MAX_DEPTH` when the moved subtree would end up too deep; `NOT_FOUND` for an unknown parent.\n\nUse it when a customer reorganizes, for example to move a project to another organization. Delegation chains are\nnot rewritten: grants inside the moved tenant keep the grant authorities they were issued under. Boundaries of the\nnew ancestors apply from the next request, because boundaries are read from the ancestry at evaluation time.\n\n```ts\nawait iam.api.tenants.reparent(rootCredential, { tenantId: projectId, parentId: otherOrganizationId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#reparent"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "parentId": {
          "type": "string"
         },
         "authorityId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "parentId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "parentId": {
             "type": "string"
            },
            "name": {
             "type": "string"
            },
            "type": {
             "type": "string"
            },
            "status": {
             "$ref": "#/components/schemas/TenantStatus"
            },
            "slug": {
             "type": "string"
            },
            "region": {
             "type": "string"
            },
            "boundary": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "authPolicy": {
             "$ref": "#/components/schemas/TenantAuthPolicy"
            },
            "accessPolicy": {
             "$ref": "#/components/schemas/TenantAccessPolicy"
            },
            "limits": {
             "$ref": "#/components/schemas/TenantLimits"
            },
            "createdAt": {
             "type": "number"
            },
            "deletedAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "parentId",
            "name",
            "type",
            "status",
            "createdAt",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/resendInvitation": {
   "post": {
    "operationId": "tenants.resendInvitation",
    "tags": [
     "tenants"
    ],
    "summary": "Sends an owner invitation again with a new token and a fresh lifetime; the earlier link stops working.",
    "description": "- **Permission:** `iam:tenants:update` on the invitation, with recent authentication.\n- **Audited as:** `iam:tenants:update`.\n- **Errors:** `CONFLICT` when the invitation was already accepted or revoked; `DELIVERY_REQUIRED` without an email\n  delivery callback; `NOT_FOUND` when the invitation is not in this tenant; `RECENT_AUTH_REQUIRED`.\n\nUse it when the owner's email expired or got lost: expired invitations can be resent, and the invitation keeps the\ngrant authority reserved for the owner. While the tenant is pending, only a root administrator can call it.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#resendinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invitationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "invitationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "invitationId": {
             "type": "string"
            },
            "email": {
             "type": "string"
            },
            "expiresAt": {
             "type": "number"
            }
           },
           "required": [
            "invitationId",
            "email",
            "expiresAt"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/revokeInvitation": {
   "post": {
    "operationId": "tenants.revokeInvitation",
    "tags": [
     "tenants"
    ],
    "summary": "Cancels an owner invitation so its link can no longer be used.",
    "description": "- **Permission:** `iam:tenants:update` on the invitation, with recent authentication.\n- **Audited as:** `iam:tenants:update`.\n- **Errors:** `CONFLICT` when the invitation was already accepted or revoked; `NOT_FOUND` when it is not in this\n  tenant; `RECENT_AUTH_REQUIRED`.\n\nThe tenant stays pending. A pending tenant can only be activated through its owner invitation, and a revoked\ninvitation cannot be re-sent, so to invite a different owner, delete the tenant with [`setStatus`](#setstatus) and\ncreate it again.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#revokeinvitation"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "invitationId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "invitationId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "boolean"
            },
            "email": {
             "type": "string"
            },
            "createdAt": {
             "type": "number"
            },
            "expiresAt": {
             "type": "number"
            },
            "authorityId": {
             "type": "string"
            },
            "consumed": {
             "type": "boolean"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            }
           },
           "required": [
            "revoked",
            "email",
            "createdAt",
            "expiresAt",
            "authorityId",
            "consumed",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/revokeSessions": {
   "post": {
    "operationId": "tenants.revokeSessions",
    "tags": [
     "tenants"
    ],
    "summary": "Signs everyone out of a tenant at once, keeping your own session unless you pass includeSelf.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication.\n- **Audited as:** `iam:tenants:update` and `tenant:revoke-sessions` (with the count and `includeSelf`).\n- **Errors:** `RECENT_AUTH_REQUIRED`.\n\nUse it for incident response. It ends every session of the tenant, role sessions assumed from it into other\ntenants, and \"view as\" sessions opened through an ended session. It does not distinguish session kinds, so\nservice-account API keys of the tenant, which are stored as sessions, end too and must be issued again. Remembered\ndevices are forgotten as well, so the next sign-in needs the second factor again; you keep yours unless\n`includeSelf`. Child tenants are not affected. The result's `revoked` is the number of sessions ended.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#revokesessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "includeSelf": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setAccessPolicy": {
   "post": {
    "operationId": "tenants.setAccessPolicy",
    "tags": [
     "tenants"
    ],
    "summary": "Sets organization-wide floors for just-in-time activation that every eligible binding in the tenant must meet.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication.\n- **Audited as:** `iam:tenants:update` and `tenant:access-policy` (with the new policy).\n- **Errors:** `INVALID_INPUT` for an unknown field, a non-boolean flag, or a value out of range;\n  `INVALID_TRANSITION` for a deleted tenant; `RECENT_AUTH_REQUIRED`.\n\nThe fields are `maxActivationMs` (one minute to seven days), `requireJustification`, `requireMfa`,\n`requireApproval`, and `approvalLifetimeMs` (how long a request waits for a decision, five minutes to thirty days;\none day when unset). A binding's effective rules are its own settings tightened by the policy: a flag applies when\neither sets it, and the maximum activation is the smaller of the two, so adopting a floor later tightens existing\nbindings without editing them. The call replaces the whole policy; `null` clears it, and flags set to `false` are\ndropped. See [tenant access policy](/docs/guides/privileged-access/elevation#tenant-access-policy).\n\n```ts\nawait iam.api.tenants.setAccessPolicy(credential, {\n  tenantId,\n  accessPolicy: {\n    maxActivationMs: 4 * 3_600_000,\n    requireJustification: true,\n    requireMfa: true,\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setaccesspolicy"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "accessPolicy": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "$ref": "#/components/schemas/TenantAccessPolicy"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "accessPolicy"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setAuthPolicy": {
   "post": {
    "operationId": "tenants.setAuthPolicy",
    "tags": [
     "tenants"
    ],
    "summary": "Sets the tenant's authentication policy: MFA, allowed sign-in methods, session lifetimes, password rules, network restrictions, and impersonation.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication.\n- **Audited as:** `iam:tenants:update` and `tenant:auth-policy` (with the new policy).\n- **Errors:** `INVALID_INPUT` for an unknown field or an out-of-range value, or for an `allowedIpRanges` that\n  leaves out your own address when you set it on your own tenant; `INVALID_TRANSITION` for a deleted tenant;\n  `RECENT_AUTH_REQUIRED`.\n\nThe policy can only tighten the deployment's configuration; the fields are listed under\n[Tenant policies and limits](#tenant-policies-and-limits). It replaces the whole policy, so carry the existing\nfields over, and `null` clears it. It applies to existing sessions on their next use: requiring MFA locks out\nsessions that did not complete it, and sessions issued from outside a new IP allowlist stop working. The\nself-lockout guard checks both the address your session was issued from and the address of this request, so you\ncannot cut yourself off from your own organization.\n\n```ts\nconst tenant = await iam.api.tenants.get(credential, { tenantId });\nawait iam.api.tenants.setAuthPolicy(credential, {\n  tenantId,\n  authPolicy: { ...tenant.authPolicy, requireMfa: true, sessionIdleTimeoutMs: 30 * 60_000 },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setauthpolicy"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "authPolicy": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "$ref": "#/components/schemas/TenantAuthPolicy"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "authPolicy"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setBoundary": {
   "post": {
    "operationId": "tenants.setBoundary",
    "tags": [
     "tenants"
    ],
    "summary": "Sets a root-controlled permissions boundary on a tenant, capping what anyone in it or in any tenant below it may do.",
    "description": "- **Permission:** `iam:boundaries:update` on the tenant, with recent authentication, and you must be a root\n  administrator.\n- **Audited as:** `iam:boundaries:update`.\n- **Errors:** `ACCESS_DENIED` for anyone but root; `INVALID_POLICY`, `INVALID_ACTION`, or `INVALID_RESOURCE_TYPE`\n  for a document the catalog rejects; `INVARIANT_VIOLATION` when the change would break an enforced\n  [access invariant](/docs/guides/governance/change-safety); `RECENT_AUTH_REQUIRED`.\n\nA <Term id=\"boundary\">boundary</Term> never grants. When a request is evaluated, the boundaries of the tenant and\nevery ancestor apply, so a boundary on an organization caps all of its projects. Use it for plan tiers or\nregulatory scopes that tenant administrators must not be able to widen. Root administrators are not limited by it.\nThe call replaces the boundary; it cannot remove one. See [boundaries](/docs/guides/authorization/policies#boundaries).\n\n```ts\n// Tenants on this plan may never use the billing export, whatever their roles say.\nawait iam.api.tenants.setBoundary(rootCredential, {\n  tenantId,\n  boundary: {\n    version: 1,\n    statements: [\n      { effect: 'allow', actions: ['*'], resources: ['*'] },\n      { effect: 'deny', actions: ['billing:export'], resources: ['*'] },\n    ],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setboundary"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "boundary": {
          "$ref": "#/components/schemas/PolicyDocument"
         }
        },
        "required": [
         "tenantId",
         "boundary"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "boundary": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "name": {
             "type": "string"
            },
            "type": {
             "type": "string"
            },
            "parentId": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "string"
              }
             ]
            },
            "status": {
             "$ref": "#/components/schemas/TenantStatus"
            },
            "slug": {
             "type": "string"
            },
            "region": {
             "type": "string"
            },
            "authPolicy": {
             "$ref": "#/components/schemas/TenantAuthPolicy"
            },
            "accessPolicy": {
             "$ref": "#/components/schemas/TenantAccessPolicy"
            },
            "limits": {
             "$ref": "#/components/schemas/TenantLimits"
            },
            "createdAt": {
             "type": "number"
            },
            "deletedAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "boundary",
            "name",
            "type",
            "parentId",
            "status",
            "createdAt",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setLimits": {
   "post": {
    "operationId": "tenants.setLimits",
    "tags": [
     "tenants"
    ],
    "summary": "Sets the tenant's plan limits, root only: how many people, service accounts, groups, roles, policies, registered resources, and webhooks it may hold.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, and you must be a root administrator.\n- **Audited as:** `iam:tenants:update` and `tenant:limits` (with the new limits). A caller who is not root is\n  recorded as a denial.\n- **Errors:** `ACCESS_DENIED` for anyone but root; `INVALID_INPUT` for an unknown key or a value that is not a whole\n  number from 0 to 1,000,000,000; `INVALID_TRANSITION` for a deleted tenant.\n\nOmitted keys are unlimited and `null` clears every limit. Lowering a limit below current usage removes nothing; it\nonly blocks further creation. The member limit counts active and disabled people, not deleted tombstones. Show\ncurrent usage next to the limits with [`usage`](#usage).\n\n```ts\nawait iam.api.tenants.setLimits(rootCredential, {\n  tenantId,\n  limits: { identities: 25, serviceAccounts: 5, webhooks: 3 },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setlimits"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "limits": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "$ref": "#/components/schemas/TenantLimits"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "limits"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setRegion": {
   "post": {
    "operationId": "tenants.setRegion",
    "tags": [
     "tenants"
    ],
    "summary": "Moves a tenant's home region in a multi-region deployment, root only, so its sign-in is served by that region from then on.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication, and you must be a root\n  administrator.\n- **Audited as:** `iam:tenants:update` and `tenant:region` (with the effective region before and after). A caller who\n  is not root is recorded as a denial.\n- **Errors:** `ACCESS_DENIED` for anyone but root; `INVALID_INPUT` for a region the deployment does not know, when\n  regions are not configured, or for `null` on the root tenant; `INVALID_TRANSITION` for a deleted tenant;\n  `RECENT_AUTH_REQUIRED`.\n\nDescendants without a region of their own move with it; `null` makes the tenant inherit its parent's region again.\nThe call changes where sign-in is served. When your regions share one database that is the whole move; when each\nregion keeps its own database, copy the organization's data to the new region yourself before switching.\n\n```ts\nawait iam.api.tenants.setRegion(rootCredential, { tenantId, region: 'eu-west-1' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setregion"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "region": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "region"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setSlug": {
   "post": {
    "operationId": "tenants.setSlug",
    "tags": [
     "tenants"
    ],
    "summary": "Sets, changes, or removes the tenant's public sign-in alias.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication.\n- **Audited as:** `iam:tenants:update`.\n- **Errors:** `SLUG_TAKEN` (409) when another tenant holds the alias; `INVALID_INPUT` for a badly formed slug;\n  `INVALID_TRANSITION` for a deleted tenant; `RECENT_AUTH_REQUIRED`.\n\nA slug is 1 to 63 lowercase letters, digits, or hyphens, neither starting nor ending with a hyphen, and unique\nacross the whole installation; input is lowercased. The previous alias is released at once, so links and bookmarks\nthat use it stop resolving and another tenant may claim it. `slug: null` removes the alias.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setslug"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "slug": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "slug"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Tenant"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/setStatus": {
   "post": {
    "operationId": "tenants.setStatus",
    "tags": [
     "tenants"
    ],
    "summary": "Suspends, reactivates, or deletes a tenant together with its whole subtree.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant to suspend or reactivate, `iam:tenants:delete` to delete;\n  recent authentication either way.\n- **Audited as:** the action checked, `iam:tenants:update` or `iam:tenants:delete`.\n- **Errors:** `INVALID_TRANSITION` for the root tenant, a deleted tenant, a pending tenant (which can only be\n  deleted), or an unknown status; `TENANT_INACTIVE` when reactivating under a parent that is not active;\n  `RECENT_AUTH_REQUIRED`.\n\nWhat each change does:\n\n- **Suspend** makes the tenant and every descendant unusable at once: sign-in and authorization are refused, and\n  every session and API key in the subtree is deleted, including role sessions sourced from it. Descendants keep\n  their own status, so they become usable again when the tenant is reactivated.\n- **Reactivate** lets people sign in again. Deleted sessions and API keys are not restored.\n- **Delete** marks the tenant and every descendant `deleted`, stamps `deletedAt` to start the retention window, and\n  deletes their sessions. The records stay until the retention worker (`purgeDeleted`, the `purge` CLI command,\n  30 days by default) removes them; audit records survive the purge. A deleted tenant cannot be changed again.\n\nBecause authorization inside a suspended tenant is refused, only a root administrator can reactivate or delete it.\nSee [scheduled jobs](/docs/operations/jobs) for running the retention worker.\n\n```ts\nawait iam.api.tenants.setStatus(rootCredential, { tenantId, status: 'suspended' });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#setstatus"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "status": {
          "type": "string",
          "enum": [
           "active",
           "deleted",
           "suspended"
          ]
         }
        },
        "required": [
         "tenantId",
         "status"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deletedAt": {
             "type": "number"
            },
            "status": {
             "type": "string",
             "enum": [
              "active",
              "deleted",
              "suspended"
             ]
            },
            "name": {
             "type": "string"
            },
            "type": {
             "type": "string"
            },
            "parentId": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "string"
              }
             ]
            },
            "slug": {
             "type": "string"
            },
            "region": {
             "type": "string"
            },
            "boundary": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "authPolicy": {
             "$ref": "#/components/schemas/TenantAuthPolicy"
            },
            "accessPolicy": {
             "$ref": "#/components/schemas/TenantAccessPolicy"
            },
            "limits": {
             "$ref": "#/components/schemas/TenantLimits"
            },
            "createdAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "status",
            "name",
            "type",
            "parentId",
            "createdAt",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/update": {
   "post": {
    "operationId": "tenants.update",
    "tags": [
     "tenants"
    ],
    "summary": "Renames a tenant.",
    "description": "- **Permission:** `iam:tenants:update` on the tenant, with recent authentication.\n- **Audited as:** `iam:tenants:update`.\n- **Errors:** `INVALID_INPUT` for an empty name; `INVALID_TRANSITION` for a deleted tenant; `RECENT_AUTH_REQUIRED`.\n\nOther properties have their own calls: [`setSlug`](#setslug), [`setAuthPolicy`](#setauthpolicy),\n[`setAccessPolicy`](#setaccesspolicy), [`setLimits`](#setlimits), [`setBoundary`](#setboundary),\n[`reparent`](#reparent), and [`setStatus`](#setstatus).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "name"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "name": {
             "type": "string"
            },
            "type": {
             "type": "string"
            },
            "parentId": {
             "anyOf": [
              {
               "type": "null"
              },
              {
               "type": "string"
              }
             ]
            },
            "status": {
             "$ref": "#/components/schemas/TenantStatus"
            },
            "slug": {
             "type": "string"
            },
            "region": {
             "type": "string"
            },
            "boundary": {
             "$ref": "#/components/schemas/PolicyDocument"
            },
            "authPolicy": {
             "$ref": "#/components/schemas/TenantAuthPolicy"
            },
            "accessPolicy": {
             "$ref": "#/components/schemas/TenantAccessPolicy"
            },
            "limits": {
             "$ref": "#/components/schemas/TenantLimits"
            },
            "createdAt": {
             "type": "number"
            },
            "deletedAt": {
             "type": "number"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "name",
            "type",
            "parentId",
            "status",
            "createdAt",
            "id",
            "tenantId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/tenants/usage": {
   "post": {
    "operationId": "tenants.usage",
    "tags": [
     "tenants"
    ],
    "summary": "Returns the tenant's current record counts next to its plan limits, for plan pages and metering.",
    "description": "- **Permission:** `iam:tenants:read` on the tenant.\n- **Audited as:** `iam:tenants:read`.\n\nIt counts people (`identities`: active and disabled, not deleted) and how many of them have MFA (`mfaEnrolled`: an\nenabled authenticator or at least one passkey), service accounts, groups, roles, policies, registered resources,\nrelationships, webhooks, and unexpired user sessions (`activeSessions`). `limits` is `{}` when none are set. The\ncounts are read from storage on every call, so cache them for dashboards rather than calling this on every\nrequest.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/tenants#usage"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "tenantId": {
             "type": "string"
            },
            "identities": {
             "type": "number"
            },
            "mfaEnrolled": {
             "type": "number"
            },
            "serviceAccounts": {
             "type": "number"
            },
            "groups": {
             "type": "number"
            },
            "roles": {
             "type": "number"
            },
            "policies": {
             "type": "number"
            },
            "resources": {
             "type": "number"
            },
            "relationships": {
             "type": "number"
            },
            "webhooks": {
             "type": "number"
            },
            "activeSessions": {
             "type": "number"
            },
            "limits": {
             "$ref": "#/components/schemas/TenantLimits"
            }
           },
           "required": [
            "tenantId",
            "identities",
            "mfaEnrolled",
            "serviceAccounts",
            "groups",
            "roles",
            "policies",
            "resources",
            "relationships",
            "webhooks",
            "activeSessions",
            "limits"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/create": {
   "post": {
    "operationId": "trust.create",
    "tags": [
     "trust"
    ],
    "summary": "Creates a trust that lets one source identity assume one role of this tenant.",
    "description": "- **Permission:** `iam:trust:create` on the role (`iam/{roleId}`); root administrators only, with recent\n  authentication.\n- **Audited as:** `iam:trust:create`.\n- **Errors:** `ACCESS_DENIED` for anyone but a root administrator; `RECENT_AUTH_REQUIRED` without recent\n  authentication; `PROTECTED_RESOURCE` when the role is the Owner role; `NOT_FOUND` when the role is not in this\n  tenant or the source identity is not in `sourceTenantId`; `INVALID_POLICY`, `INVALID_ACTION`, or\n  `INVALID_RESOURCE_TYPE` for a `ceiling` storage would reject; `INVARIANT_VIOLATION` when the trust would newly\n  break an enforced access invariant.\n\nTrusts are platform-controlled because they cross tenant boundaries: a tenant administrator cannot open their\ntenant to an outside identity on their own. Keep `requireMfa` on; [`analysis.findings`](/docs/reference/api/analysis#findings)\nreports trusts without it. Give each trust the narrowest ceiling the task needs.\n\nWith `kind: 'web-identity'` the call creates a [web-identity trust](#web-identity-trusts) instead: `iam:trust:create`\non the role without root, recent authentication, and `sts.webIdentity.enabled` (else `FEATURE_DISABLED`). It fails\nwith `WEAK_TRUST_CONDITIONS` when the conditions do not pin `token.sub`, `INVALID_IDENTITY` when the service account\nis not an active service account, and `INVALID_INPUT` for identity-trust fields such as `sourceTenantId` or\n`requireMfa`. Both kinds return the public trust, which reports `requiresExternalId` instead of the stored hash.\n\n```ts\nconst trust = await iam.api.trust.create(rootCredential, {\n  tenantId: customerTenantId,\n  sourceTenantId: rootTenantId,\n  sourceIdentityId: supportEngineerId,\n  roleId: supportRoleId,\n  externalId: 'ticket-routing-7f3a',\n  ceiling: {\n    version: 1,\n    statements: [{ effect: 'allow', actions: ['iam:identities:read', 'iam:audit:read'], resources: ['*'] }],\n  },\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustCreateInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicTrust"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/evaluateWebIdentity": {
   "post": {
    "operationId": "trust.evaluateWebIdentity",
    "tags": [
     "trust"
    ],
    "summary": "Checks what a web-identity trust would conclude about an external token, without issuing anything.",
    "description": "- **Permission:** `iam:trust:read` on the trust, and `sts.webIdentity.enabled`.\n- **Audited as:** `iam:trust:read`.\n- **Errors:** `FEATURE_DISABLED` when web identity is off; `INVALID_INPUT` for a trust that is not a web-identity\n  trust; `NOT_FOUND`.\n\nThe public exchange answers every refusal with the same `WEB_IDENTITY_REJECTED`, so callers cannot probe which\ntrusts exist. This dry run is how administrators find out why a token is refused: it returns `verified`, the\n`reason` the exchange would give (a verification failure such as `audience`, `expired`, or `unknown-key`, or\n`conditions` or `source-identity`), the verified registered claims, each failing condition as `Operator:key`, and\nthe session tags and source identity the claim mappings would produce. No replay record is written, so the token\nstays redeemable.\n\n```ts\nconst result = await iam.api.trust.evaluateWebIdentity(credential, { tenantId, trustId, webIdentityToken });\nif (result.conditions && !result.conditions.matched) console.log(result.conditions.failed); // ['StringLike:token.sub']\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#evaluatewebidentity"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "trustId": {
          "type": "string"
         },
         "webIdentityToken": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "trustId",
         "webIdentityToken"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/WebIdentityEvaluation"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/list": {
   "post": {
    "operationId": "trust.list",
    "tags": [
     "trust"
    ],
    "summary": "Lists the trusts that target this tenant's roles.",
    "description": "- **Permission:** `iam:trust:read` on the tenant.\n- **Audited as:** `iam:trust:read`.\n\nRevoked trusts are left out unless `includeRevoked` is `true`. External ID hashes are never returned; instead,\n`requiresExternalId` tells you whether callers must present one. Tenant administrators can use this to see who\noutside the tenant may assume which role. Trusts of kind `web-identity`, which admit tokens from an external OpenID\nConnect provider, appear here too.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "includeRevoked": {
          "type": "boolean"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/PublicTrust"
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/revoke": {
   "post": {
    "operationId": "trust.revoke",
    "tags": [
     "trust"
    ],
    "summary": "Revokes a trust so its source can no longer assume the role, and ends the role sessions already issued under it.",
    "description": "- **Permission:** `iam:trust:revoke` on the trust (`iam/{trustId}`); root administrators only, with recent\n  authentication.\n- **Audited as:** `iam:trust:revoke`.\n- **Errors:** `ACCESS_DENIED` for anyone but a root administrator; `RECENT_AUTH_REQUIRED` without recent\n  authentication; `NOT_FOUND` when the trust is not in this tenant.\n\nExisting role sessions under the trust are refused at their next use. The record is kept with `revoked: true`, so\nit still appears with `includeRevoked` and in the audit history. Revoking an already revoked trust succeeds.\n\nThe trust's live role sessions are also deleted in the same transaction, so they disappear from\n[`roles.listSessions`](/docs/reference/api/roles#listsessions) at once. A web-identity trust is revoked by the\nadministrator whose authority created it (or root) rather than root only, and needs `sts.webIdentity.enabled`\n(`FEATURE_DISABLED` otherwise).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#revoke"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "trustId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "trustId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicTrust"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/revokeSessions": {
   "post": {
    "operationId": "trust.revokeSessions",
    "tags": [
     "trust"
    ],
    "summary": "Ends the role sessions issued under one trust before a point in time, without revoking the trust.",
    "description": "- **Permission:** `iam:roles:revoke-sessions` on the trust's role (`iam/{roleId}`), with recent authentication. It\n  is not root-only, so the target tenant's administrators can end sessions under a platform-controlled trust.\n- **Audited as:** `iam:roles:revoke-sessions` and `role:sessions-revoked` (with the watermark and the number of\n  sessions deleted).\n- **Errors:** `INVALID_INPUT` when `before` is not a whole number of milliseconds, is negative, or lies in the future;\n  `NOT_FOUND` when the trust is not in this tenant; `RECENT_AUTH_REQUIRED`; `ACCESS_DENIED`.\n\n`before` defaults to now, which ends every session issued so far. The trust's `sessionsRevokedBefore` watermark only\nmoves forward, so older sessions are refused at their next use and the matching rows are deleted at once, while new\nassumptions keep working. Session JWTs that other services verify offline stay valid there until they expire; those\nservices can check [`sts.getCallerIdentity`](/docs/reference/api/sts#getcalleridentity) for an online answer.\n\n```ts\nconst { revoked } = await iam.api.trust.revokeSessions(credential, { tenantId, trustId });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#revokesessions"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustRevokeSessionsInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "trustId": {
             "type": "string"
            },
            "sessionsRevokedBefore": {
             "type": "number"
            },
            "revoked": {
             "type": "number"
            }
           },
           "required": [
            "trustId",
            "sessionsRevokedBefore",
            "revoked"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/trust/update": {
   "post": {
    "operationId": "trust.update",
    "tags": [
     "trust"
    ],
    "summary": "Changes a trust's safeguards and options, such as its MFA requirement, ceiling, session length, attribute passing, tag keys, source identity mode, or a web-identity trust's conditions and claim mappi…",
    "description": "- **Permission:** `iam:trust:update` on the trust, with recent authentication. Identity trusts need a root\n  administrator; web-identity trusts need the grant authority that created them (or root).\n- **Audited as:** `iam:trust:update`.\n- **Errors:** `ACCESS_DENIED` for anyone else; `CONFLICT` (409) for a revoked trust; `INVALID_INPUT` when nothing\n  changes or a field belongs to the other kind of trust (`conditions`, `tagClaims`, or `sourceIdentityClaim` on an\n  identity trust; `requireMfa`, `allowedTagKeys`, or `sourceIdentityMode` on a web-identity trust);\n  `WEAK_TRUST_CONDITIONS` for conditions that do not pin `token.sub`; `FEATURE_DISABLED` for a web-identity trust\n  while web identity is off; `RECENT_AUTH_REQUIRED`; `NOT_FOUND`.\n\nOnly the fields you pass change, and `null` returns `ceiling`, `maxSessionSeconds`, `allowedTagKeys`,\n`description`, `tagClaims`, or `sourceIdentityClaim` to its default. Tightening what the trust admits (its tag keys,\nsource identity mode, conditions, claim mappings, or a shorter `maxSessionSeconds`) also moves the trust's\n`sessionsRevokedBefore` watermark to now, so sessions issued under the looser rules end at their next use. Use it\nto turn off `passSourceAttributes` on older cross-tenant trusts.\n\n```ts\nawait iam.api.trust.update(rootCredential, { tenantId, trustId, passSourceAttributes: false, maxSessionSeconds: 900 });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/trust#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TrustUpdateInput"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PublicTrust"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/create": {
   "post": {
    "operationId": "webhooks.create",
    "tags": [
     "webhooks"
    ],
    "summary": "Subscribes an HTTPS endpoint to the tenant's audit events and returns the signing secret, which is shown only once.",
    "description": "- **Permission:** `iam:webhooks:create` on the tenant, with recent authentication.\n- **Audited as:** `iam:webhooks:create`.\n- **Errors:** `INVALID_INPUT` for a URL that is not absolute HTTPS, carries credentials or a fragment, or for invalid\n  `events`, `outcomes`, or `resources`; `LIMIT_EXCEEDED` at 50 subscriptions or the tenant's plan limit;\n  `ACCESS_DENIED` for `scope: 'subtree'` unless the caller is the platform root; `RECENT_AUTH_REQUIRED` when the\n  caller has not authenticated recently or uses a temporary credential; `IMPERSONATION_RESTRICTED` in a \"view as\"\n  session.\n\nStore the returned `secret` (it starts with `whsec_`) in your endpoint's configuration right away: only a sealed copy\nis kept, and no call returns it again. Plain HTTP is accepted only for `localhost`, `127.0.0.1`, or `[::1]` while the\ndeployment itself does not run on HTTPS, which is enough for local development. The subscription starts active.\n\n```ts\nconst { webhook, secret } = await iam.api.webhooks.create(credential, {\n  tenantId,\n  url: 'https://siem.example.com/hooks/iam',\n  events: ['iam:*', 'binding:*', 'auth:signin:fail'],\n  outcomes: ['deny'],\n  description: 'Security feed',\n});\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#create"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "url": {
          "type": "string"
         },
         "events": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "description": {
          "type": "string"
         },
         "scope": {
          "type": "string",
          "enum": [
           "tenant",
           "subtree"
          ]
         },
         "outcomes": {
          "type": "array",
          "items": {
           "type": "string",
           "enum": [
            "allow",
            "deny"
           ]
          }
         },
         "resources": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        },
        "required": [
         "tenantId",
         "url",
         "events"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "webhook": {
             "type": "object",
             "properties": {
              "url": {
               "type": "string"
              },
              "events": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "description": {
               "type": "string"
              },
              "active": {
               "type": "boolean"
              },
              "scope": {
               "type": "string",
               "enum": [
                "tenant",
                "subtree"
               ]
              },
              "outcomes": {
               "type": "array",
               "items": {
                "type": "string",
                "enum": [
                 "allow",
                 "deny"
                ]
               }
              },
              "resources": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "createdAt": {
               "type": "number"
              },
              "updatedAt": {
               "type": "number"
              },
              "createdBy": {
               "type": "string"
              },
              "id": {
               "type": "string"
              },
              "tenantId": {
               "type": "string"
              },
              "uniqueKey": {
               "type": "string"
              }
             },
             "required": [
              "url",
              "events",
              "active",
              "scope",
              "createdAt",
              "updatedAt",
              "createdBy",
              "id",
              "tenantId"
             ],
             "additionalProperties": {}
            },
            "secret": {
             "type": "string"
            }
           },
           "required": [
            "webhook",
            "secret"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/delete": {
   "post": {
    "operationId": "webhooks.delete",
    "tags": [
     "webhooks"
    ],
    "summary": "Deletes a subscription and discards the deliveries still waiting to be sent.",
    "description": "- **Permission:** `iam:webhooks:delete` on the webhook, with recent authentication.\n- **Audited as:** `iam:webhooks:delete`.\n- **Errors:** `NOT_FOUND` when the webhook is not in this tenant.\n\nDelivered and abandoned deliveries stay in storage until the retention sweep removes them. To stop deliveries\ntemporarily and keep the secret, pause the subscription with [`update`](#update) instead.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#delete"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deleted": {
             "type": "boolean"
            }
           },
           "required": [
            "deleted"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/get": {
   "post": {
    "operationId": "webhooks.get",
    "tags": [
     "webhooks"
    ],
    "summary": "Returns one subscription without its secret.",
    "description": "- **Permission:** `iam:webhooks:read` on the webhook.\n- **Audited as:** `iam:webhooks:read`.\n- **Errors:** `NOT_FOUND` when the webhook is not in this tenant.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#get"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "url": {
             "type": "string"
            },
            "events": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "description": {
             "type": "string"
            },
            "active": {
             "type": "boolean"
            },
            "scope": {
             "type": "string",
             "enum": [
              "tenant",
              "subtree"
             ]
            },
            "outcomes": {
             "type": "array",
             "items": {
              "type": "string",
              "enum": [
               "allow",
               "deny"
              ]
             }
            },
            "resources": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "createdAt": {
             "type": "number"
            },
            "updatedAt": {
             "type": "number"
            },
            "createdBy": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "url",
            "events",
            "active",
            "scope",
            "createdAt",
            "updatedAt",
            "createdBy",
            "id",
            "tenantId"
           ],
           "additionalProperties": {}
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/list": {
   "post": {
    "operationId": "webhooks.list",
    "tags": [
     "webhooks"
    ],
    "summary": "Lists every subscription of the tenant, without secrets.",
    "description": "- **Permission:** `iam:webhooks:read` on the tenant.\n- **Audited as:** `iam:webhooks:read`.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#list"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "url": {
              "type": "string"
             },
             "events": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "description": {
              "type": "string"
             },
             "active": {
              "type": "boolean"
             },
             "scope": {
              "type": "string",
              "enum": [
               "tenant",
               "subtree"
              ]
             },
             "outcomes": {
              "type": "array",
              "items": {
               "type": "string",
               "enum": [
                "allow",
                "deny"
               ]
              }
             },
             "resources": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "createdAt": {
              "type": "number"
             },
             "updatedAt": {
              "type": "number"
             },
             "createdBy": {
              "type": "string"
             },
             "id": {
              "type": "string"
             },
             "tenantId": {
              "type": "string"
             },
             "uniqueKey": {
              "type": "string"
             }
            },
            "required": [
             "url",
             "events",
             "active",
             "scope",
             "createdAt",
             "updatedAt",
             "createdBy",
             "id",
             "tenantId"
            ],
            "additionalProperties": {}
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/listDeliveries": {
   "post": {
    "operationId": "webhooks.listDeliveries",
    "tags": [
     "webhooks"
    ],
    "summary": "Returns the delivery history of one subscription, newest first, with status, attempt count, and the last error.",
    "description": "- **Permission:** `iam:webhooks:read` on the webhook.\n- **Audited as:** `iam:webhooks:read`.\n- **Errors:** `NOT_FOUND` when the webhook is not in this tenant; `INVALID_INPUT` when `limit` is outside 1 to 1000.\n\nEach entry has the delivery `id`, the `event` name, the audit `eventId` it carried, `createdAt`, `attempts`,\n`deliveredAt`, `failedAt`, `lastError`, and a `status` of `pending`, `delivered`, or `failed`. `limit` defaults to 100.\nPayloads are never returned. Use it to diagnose a failing endpoint (`lastError` holds the HTTP status or network\nerror) and to find the deliveries to [`redeliver`](#redeliver) after an outage. Finished deliveries stay listed until\n`iam.sweepExpired()` removes them after its delivery retention period (30 days by default).",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#listdeliveries"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         },
         "limit": {
          "type": "number"
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "event": {
              "type": "string"
             },
             "eventId": {
              "type": "string"
             },
             "createdAt": {
              "type": "number"
             },
             "attempts": {
              "type": "number"
             },
             "deliveredAt": {
              "type": "number"
             },
             "failedAt": {
              "type": "number"
             },
             "lastError": {
              "type": "string"
             },
             "status": {
              "$ref": "#/components/schemas/DeliveryStatus"
             }
            },
            "required": [
             "id",
             "event",
             "eventId",
             "createdAt",
             "attempts",
             "deliveredAt",
             "failedAt",
             "lastError",
             "status"
            ],
            "additionalProperties": false
           }
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/ping": {
   "post": {
    "operationId": "webhooks.ping",
    "tags": [
     "webhooks"
    ],
    "summary": "Queues a synthetic webhook:ping delivery so you can check an endpoint and its signature verification end to end.",
    "description": "- **Permission:** `iam:webhooks:update` on the webhook.\n- **Audited as:** `iam:webhooks:update`.\n- **Errors:** `INVALID_TRANSITION` when the subscription is paused; `NOT_FOUND` when the webhook is not in this\n  tenant.\n\nThe ping is sent whatever the subscription's event patterns are. Its body has `type: 'webhook:ping'`, the caller as\n`actorId`, and the webhook id as `resourceId`. It is not an audit event, so it cannot be redelivered. The call returns\nthe `deliveryId`; follow it with [`listDeliveries`](#listdeliveries) once the outbox worker has run.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#ping"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deliveryId": {
             "type": "string"
            }
           },
           "required": [
            "deliveryId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/redeliver": {
   "post": {
    "operationId": "webhooks.redeliver",
    "tags": [
     "webhooks"
    ],
    "summary": "Queues the event behind an earlier delivery again, rebuilt from the audit log and signed with the current secret.",
    "description": "- **Permission:** `iam:webhooks:update` on the webhook.\n- **Audited as:** `iam:webhooks:update`.\n- **Errors:** `NOT_FOUND` when the delivery does not belong to this webhook, or when its audit event no longer exists\n  (for example after `pruneAudit`); `INVALID_TRANSITION` when the subscription is paused or the delivery was a ping.\n\nUse it after an endpoint outage outlasted the retries, or to replay an event your consumer lost. It works on any\ndelivery, whatever its status, and creates a new delivery id, so your endpoint must deduplicate on the body's `id`\n(the audit event id, returned here as `eventId`).\n\n```ts\nconst failed = (await iam.api.webhooks.listDeliveries(credential, { tenantId, webhookId }))\n  .filter((delivery) => delivery.status === 'failed');\nfor (const delivery of failed)\n  await iam.api.webhooks.redeliver(credential, { tenantId, webhookId, deliveryId: delivery.id });\n```",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#redeliver"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         },
         "deliveryId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "webhookId",
         "deliveryId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "deliveryId": {
             "type": "string"
            },
            "eventId": {
             "type": "string"
            }
           },
           "required": [
            "deliveryId",
            "eventId"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/rotateSecret": {
   "post": {
    "operationId": "webhooks.rotateSecret",
    "tags": [
     "webhooks"
    ],
    "summary": "Replaces a subscription's signing secret and returns the new one, which is shown only once.",
    "description": "- **Permission:** `iam:webhooks:update` on the webhook, with recent authentication.\n- **Audited as:** `iam:webhooks:update`.\n- **Errors:** `NOT_FOUND` when the webhook is not in this tenant; `RECENT_AUTH_REQUIRED`;\n  `IMPERSONATION_RESTRICTED`.\n\nDeliveries are signed when they are sent, not when they are queued, so every delivery sent after this call, including\nretries of older events, uses the new secret. There is no overlap period: update your endpoint right away, or let it\naccept either secret while you switch. Rotate when a secret may have leaked or when the person who configured the\nendpoint leaves.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#rotatesecret"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "webhook": {
             "type": "object",
             "properties": {
              "url": {
               "type": "string"
              },
              "events": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "description": {
               "type": "string"
              },
              "active": {
               "type": "boolean"
              },
              "scope": {
               "type": "string",
               "enum": [
                "tenant",
                "subtree"
               ]
              },
              "outcomes": {
               "type": "array",
               "items": {
                "type": "string",
                "enum": [
                 "allow",
                 "deny"
                ]
               }
              },
              "resources": {
               "type": "array",
               "items": {
                "type": "string"
               }
              },
              "createdAt": {
               "type": "number"
              },
              "updatedAt": {
               "type": "number"
              },
              "createdBy": {
               "type": "string"
              },
              "id": {
               "type": "string"
              },
              "tenantId": {
               "type": "string"
              },
              "uniqueKey": {
               "type": "string"
              }
             },
             "required": [
              "url",
              "events",
              "active",
              "scope",
              "createdAt",
              "updatedAt",
              "createdBy",
              "id",
              "tenantId"
             ],
             "additionalProperties": {}
            },
            "secret": {
             "type": "string"
            }
           },
           "required": [
            "webhook",
            "secret"
           ],
           "additionalProperties": false
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/webhooks/update": {
   "post": {
    "operationId": "webhooks.update",
    "tags": [
     "webhooks"
    ],
    "summary": "Changes a subscription's URL, event patterns, filters, description, or active flag.",
    "description": "- **Permission:** `iam:webhooks:update` on the webhook, with recent authentication.\n- **Audited as:** `iam:webhooks:update`.\n- **Errors:** `INVALID_INPUT` when nothing is given to change or a value is invalid (the same rules as\n  [`create`](#create)); `NOT_FOUND`; `RECENT_AUTH_REQUIRED`; `IMPERSONATION_RESTRICTED`.\n\nPass `null` for `description`, `outcomes`, or `resources` to clear it. The `scope` cannot change after creation.\n`active: false` pauses the subscription: new events are not queued for it, and deliveries already queued are dropped\nrather than sent. Setting `active: true` resumes it for new events only; use [`redeliver`](#redeliver) for anything\nyou still need from the paused period's queued deliveries.",
    "externalDocs": {
     "url": "http://localhost:4000/docs/reference/api/webhooks#update"
    },
    "parameters": [
     {
      "$ref": "#/components/parameters/XBetterIam"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     },
     {
      "sessionCookie": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "tenantId": {
          "type": "string"
         },
         "webhookId": {
          "type": "string"
         },
         "url": {
          "type": "string"
         },
         "events": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "description": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "string"
           }
          ]
         },
         "active": {
          "type": "boolean"
         },
         "outcomes": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "array",
            "items": {
             "type": "string",
             "enum": [
              "allow",
              "deny"
             ]
            }
           }
          ]
         },
         "resources": {
          "anyOf": [
           {
            "type": "null"
           },
           {
            "type": "array",
            "items": {
             "type": "string"
            }
           }
          ]
         }
        },
        "required": [
         "tenantId",
         "webhookId"
        ],
        "additionalProperties": false
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success. The result is wrapped in `data`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "url": {
             "type": "string"
            },
            "events": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "description": {
             "type": "string"
            },
            "active": {
             "type": "boolean"
            },
            "scope": {
             "type": "string",
             "enum": [
              "tenant",
              "subtree"
             ]
            },
            "outcomes": {
             "type": "array",
             "items": {
              "type": "string",
              "enum": [
               "allow",
               "deny"
              ]
             }
            },
            "resources": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "createdAt": {
             "type": "number"
            },
            "updatedAt": {
             "type": "number"
            },
            "createdBy": {
             "type": "string"
            },
            "id": {
             "type": "string"
            },
            "tenantId": {
             "type": "string"
            },
            "uniqueKey": {
             "type": "string"
            }
           },
           "required": [
            "url",
            "events",
            "active",
            "scope",
            "createdAt",
            "updatedAt",
            "createdBy",
            "id",
            "tenantId"
           ],
           "additionalProperties": {}
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "description": "A session token, API key, or assumed-role token in `Authorization: Bearer …`."
   },
   "sessionCookie": {
    "type": "apiKey",
    "in": "cookie",
    "name": "better-iam.session",
    "description": "The browser session cookie. Cookie requests must send an exact trusted Origin header."
   }
  },
  "parameters": {
   "XBetterIam": {
    "name": "X-Better-IAM",
    "in": "header",
    "required": true,
    "description": "Must be `1`. Together with the JSON content type it blocks cross-site form posts (CSRF).",
    "schema": {
     "type": "string",
     "const": "1"
    }
   }
  },
  "responses": {
   "Error": {
    "description": "An IamError. See the error codes reference.",
    "headers": {
     "Retry-After": {
      "description": "Seconds to wait, on RATE_LIMITED responses.",
      "schema": {
       "type": "integer"
      }
     },
     "X-Request-Id": {
      "description": "The request id, echoed for support.",
      "schema": {
       "type": "string"
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/ErrorEnvelope"
      }
     }
    }
   }
  },
  "schemas": {
   "CredentialInput": {
    "type": "object",
    "properties": {
     "headers": {
      "anyOf": [
       {
        "type": "array",
        "items": {
         "type": "array",
         "prefixItems": [
          {
           "type": "string"
          },
          {
           "type": "string"
          }
         ]
        }
       },
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "type": "string"
        }
       },
       {
        "$ref": "#/components/schemas/Headers"
       }
      ]
     },
     "token": {
      "type": "string"
     }
    },
    "additionalProperties": false
   },
   "Headers": {
    "type": "object",
    "properties": {}
   },
   "AccessPathsResult": {
    "type": "object",
    "properties": {
     "allowed": {
      "type": "boolean"
     },
     "reason": {
      "type": "string",
      "description": "The decision reason for the request as it stands."
     },
     "paths": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AccessPath"
      },
      "description": "Empty when allowed, or when nothing the caller can do alone would help (ask an administrator)."
     }
    },
    "required": [
     "allowed",
     "reason",
     "paths"
    ],
    "additionalProperties": false
   },
   "AccessPath": {
    "anyOf": [
     {
      "type": "object",
      "properties": {
       "kind": {
        "type": "string",
        "const": "mfa"
       }
      },
      "required": [
       "kind"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "kind": {
        "type": "string",
        "const": "accept-agreements"
       },
       "agreements": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          },
          "version": {
           "type": "number"
          }
         },
         "required": [
          "id",
          "name",
          "version"
         ],
         "additionalProperties": false
        }
       }
      },
      "required": [
       "kind",
       "agreements"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "kind": {
        "type": "string",
        "const": "activate"
       },
       "bindingId": {
        "type": "string"
       },
       "role": {
        "type": "object",
        "properties": {
         "id": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "id",
         "name"
        ],
        "additionalProperties": false
       },
       "requireApproval": {
        "type": "boolean"
       },
       "requireJustification": {
        "type": "boolean"
       },
       "requireMfa": {
        "type": "boolean"
       },
       "maxActivationMs": {
        "type": "number"
       }
      },
      "required": [
       "kind",
       "bindingId",
       "role",
       "requireApproval",
       "requireJustification",
       "requireMfa"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "kind": {
        "type": "string",
        "const": "request-package"
       },
       "package": {
        "type": "object",
        "properties": {
         "id": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "description": {
          "type": "string"
         }
        },
        "required": [
         "id",
         "name"
        ],
        "additionalProperties": false
       },
       "requireJustification": {
        "type": "boolean"
       }
      },
      "required": [
       "kind",
       "package",
       "requireJustification"
      ],
      "additionalProperties": false
     }
    ]
   },
   "AccessRequest": {
    "type": "object",
    "properties": {
     "requesterId": {
      "type": "string"
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "justification": {
      "type": "string"
     },
     "durationSeconds": {
      "type": "number"
     },
     "status": {
      "$ref": "#/components/schemas/AccessRequestStatus"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "reviewerId": {
      "type": "string"
     },
     "reviewedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     },
     "bindingIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "grantExpiresAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "requesterId",
     "roleIds",
     "status",
     "createdAt",
     "expiresAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AccessRequestStatus": {
    "type": "string",
    "enum": [
     "pending",
     "approved",
     "denied",
     "cancelled",
     "expired"
    ]
   },
   "ActionSummary": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ]
     },
     "resourceType": {
      "type": "string"
     },
     "description": {
      "type": "string"
     }
    },
    "required": [
     "name",
     "source"
    ],
    "additionalProperties": false
   },
   "ActionDefinition": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "resourceType": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "ActivityQuery": {
    "type": "object",
    "properties": {
     "limit": {
      "type": "number"
     },
     "offset": {
      "type": "number"
     },
     "from": {
      "type": "number"
     },
     "to": {
      "type": "number"
     }
    },
    "additionalProperties": false
   },
   "AuditEvent": {
    "type": "object",
    "properties": {
     "actorId": {
      "type": "string"
     },
     "action": {
      "type": "string"
     },
     "resourceId": {
      "type": "string"
     },
     "timestamp": {
      "type": "number"
     },
     "outcome": {
      "type": "string",
      "enum": [
       "allow",
       "deny"
      ]
     },
     "rootOverride": {
      "type": "boolean"
     },
     "originalActorId": {
      "type": "string"
     },
     "impersonatorId": {
      "type": "string",
      "description": "The administrator who performed the action through an impersonation session; `actorId` is the member."
     },
     "metadata": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "sessionContext": {
      "$ref": "#/components/schemas/AuditSessionContext"
     },
     "sequence": {
      "type": "number",
      "description": "Position in the tenant's hash chain, from 1; see `verifyAuditChain`."
     },
     "previousHash": {
      "type": "string"
     },
     "hash": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "actorId",
     "action",
     "resourceId",
     "timestamp",
     "outcome",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AuditSessionContext": {
    "type": "object",
    "properties": {
     "sessionId": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token",
       "delegated"
      ]
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "sourceTenantId": {
      "type": "string"
     },
     "sessionName": {
      "type": "string"
     },
     "sourceIdentity": {
      "type": "string"
     },
     "webIdentityProviderId": {
      "type": "string"
     },
     "webIdentitySubject": {
      "type": "string",
      "description": "The verified external subject of a web-identity session, truncated to 256 characters."
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt"
      ],
      "description": "Set only for session JWTs."
     },
     "agentId": {
      "type": "string",
      "description": "Delegated sessions: the agent that acted on the person's behalf, and the delegation it acted under."
     },
     "delegationId": {
      "type": "string"
     }
    },
    "required": [
     "sessionId",
     "kind"
    ],
    "additionalProperties": false
   },
   "AgentListing": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "purpose": {
      "type": "string"
     },
     "model": {
      "type": "string"
     },
     "provider": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "protocols": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "tokenAudiences": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Services outside Better IAM the agent may present a person's delegation to (`delegations.issueToken`)."
     },
     "sponsorName": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "name",
     "sponsorName"
    ],
    "additionalProperties": false
   },
   "CreateAgentInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "sponsorId": {
      "type": "string",
      "description": "The accountable person; defaults to the caller when the caller is a person of the tenant."
     },
     "expiresAt": {
      "type": "number",
      "description": "Scheduled deactivation (epoch milliseconds), as for service accounts."
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "model": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "provider": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "purpose": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "url": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "protocols": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "delegable": {
      "type": "boolean"
     },
     "maxDelegatedSessionSeconds": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "boundary": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/PolicyDocument"
       }
      ]
     },
     "tokenAudiences": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "PolicyDocument": {
    "type": "object",
    "properties": {
     "version": {
      "type": "number",
      "const": 1
     },
     "statements": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PolicyStatement"
      }
     }
    },
    "required": [
     "version",
     "statements"
    ],
    "additionalProperties": false
   },
   "PolicyStatement": {
    "type": "object",
    "properties": {
     "sid": {
      "type": "string"
     },
     "effect": {
      "type": "string",
      "enum": [
       "allow",
       "deny"
      ]
     },
     "actions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "resources": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "conditions": {
      "type": "object",
      "properties": {
       "StringEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLikeIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Bool": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateBefore": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateAfter": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "IpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NotIpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContains": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContainsAll": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Exists": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       }
      },
      "additionalProperties": false
     }
    },
    "required": [
     "effect",
     "actions",
     "resources"
    ],
    "additionalProperties": false
   },
   "ConditionValue": {
    "anyOf": [
     {},
     {},
     {},
     {}
    ]
   },
   "AgentSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "agent": {
      "$ref": "#/components/schemas/AgentProfile"
     },
     "standing": {
      "$ref": "#/components/schemas/AgentStanding"
     },
     "sponsor": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "email": {
        "type": "string"
       },
       "status": {
        "type": "string",
        "enum": [
         "active",
         "disabled",
         "deleted"
        ]
       }
      },
      "required": [
       "id",
       "name",
       "status"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "status",
     "createdAt",
     "agent",
     "standing"
    ],
    "additionalProperties": false
   },
   "AgentProfile": {
    "type": "object",
    "properties": {
     "sponsorId": {
      "type": "string",
      "description": "The person accountable for the agent (an active user of the agent's tenant)."
     },
     "model": {
      "type": "string",
      "description": "The model the agent runs on, such as `claude-opus-5-5`; exposed to policies as principal.agentModel."
     },
     "provider": {
      "type": "string",
      "description": "Who serves the model, such as `anthropic`; exposed to policies as principal.agentProvider."
     },
     "purpose": {
      "type": "string",
      "description": "What the agent is for, shown to people deciding whether to delegate to it."
     },
     "url": {
      "type": "string",
      "description": "Where the agent can be reached or read about (an http(s) URL)."
     },
     "protocols": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Agent protocols it speaks, informational (for example `mcp`, `a2a`)."
     },
     "delegable": {
      "type": "boolean",
      "description": "Whether people may delegate their access to the agent (`delegations`); true unless set to false."
     },
     "maxDelegatedSessionSeconds": {
      "type": "number",
      "description": "The longest delegated session the agent may hold, in seconds (60 to 43200; 3600 when unset)."
     },
     "boundary": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "tokenAudiences": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Services outside Better IAM the agent may present a person's delegation to (`delegations.issueToken`): audience\nURLs or URNs, `*` matching any characters. Without it the agent cannot obtain delegation tokens."
     },
     "suspended": {
      "type": "object",
      "properties": {
       "by": {
        "type": "string"
       },
       "at": {
        "type": "number"
       },
       "reason": {
        "type": "string"
       }
      },
      "required": [
       "by",
       "at"
      ],
      "additionalProperties": false,
      "description": "Set while the agent is suspended: who suspended it, when, and why (`agents.suspend`)."
     }
    },
    "required": [
     "sponsorId"
    ],
    "additionalProperties": false
   },
   "AgentStanding": {
    "type": "string",
    "enum": [
     "expired",
     "deleted",
     "ok",
     "suspended",
     "sponsor-missing",
     "sponsor-inactive"
    ]
   },
   "PublicIdentity": {
    "type": "object",
    "properties": {
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ],
      "description": "`user` (a person), `service` (a service account) or `agent` (an AI agent with a sponsor, see `agent`)."
     },
     "agent": {
      "$ref": "#/components/schemas/AgentProfile"
     },
     "email": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      },
      "description": "Typed attributes declared by `permissions.identityAttributes`, exposed to policies as principal.{name}."
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ],
      "description": "deleted identities are tombstones: no credentials, factors, bindings, or email, kept so audit records stay resolvable."
     },
     "emailVerified": {
      "type": "boolean"
     },
     "rootAdmin": {
      "type": "boolean"
     },
     "owner": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     },
     "passwordChangedAt": {
      "type": "number",
      "description": "When the current password was set; drives `passwordMaxAgeDays` (identities without it use `createdAt`)."
     },
     "phone": {
      "type": "string"
     },
     "phoneVerified": {
      "type": "boolean"
     },
     "expiresAt": {
      "type": "number",
      "description": "Scheduled deactivation (epoch milliseconds) for contractors and temporary service accounts: past this time the\nidentity's credentials are refused and the retention worker disables it (`identity:expire`)."
     },
     "managerId": {
      "type": "string",
      "description": "The person's manager within the tenant: approvals may route to them, and offboarding hands reports to a successor."
     },
     "deletedAt": {
      "type": "number"
     },
     "deletedEmail": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "kind",
     "name",
     "status",
     "emailVerified",
     "rootAdmin",
     "owner",
     "createdAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AgentDirectoryEntry": {
    "type": "object",
    "properties": {
     "agentId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "card": {
      "allOf": [
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {}
       },
       {
        "type": "object",
        "properties": {
         "signatures": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "protected": {
             "type": "string"
            },
            "signature": {
             "type": "string"
            }
           },
           "required": [
            "protected",
            "signature"
           ],
           "additionalProperties": false
          }
         }
        },
        "required": [
         "signatures"
        ],
        "additionalProperties": false
       }
      ]
     },
     "attestation": {
      "$ref": "#/components/schemas/AgentAttestation"
     },
     "expiresAt": {
      "type": "number"
     }
    },
    "required": [
     "agentId",
     "name",
     "card",
     "attestation",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "AgentAttestation": {
    "type": "object",
    "properties": {
     "issuer": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "organization": {
      "type": "string",
      "description": "The tenant's name; also the card's `provider.organization`."
     },
     "agentId": {
      "type": "string"
     },
     "agentName": {
      "type": "string"
     },
     "sponsored": {
      "type": "boolean",
      "const": true,
      "description": "Always true: IAM signs cards only for agents whose sponsor is an active person."
     },
     "delegable": {
      "type": "boolean",
      "description": "Whether people may delegate to the agent."
     },
     "model": {
      "type": "string"
     },
     "provider": {
      "type": "string"
     },
     "protocols": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "issuedAt": {
      "type": "string",
      "description": "ISO 8601 times; verifiers refuse the card after `expiresAt`."
     },
     "expiresAt": {
      "type": "string"
     }
    },
    "required": [
     "issuer",
     "tenantId",
     "organization",
     "agentId",
     "agentName",
     "sponsored",
     "delegable",
     "issuedAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "AgentDetail": {
    "type": "object",
    "properties": {
     "keys": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "createdAt": {
         "type": "number"
        },
        "expiresAt": {
         "type": "number"
        },
        "lastUsedAt": {
         "type": "number"
        }
       },
       "required": [
        "id",
        "createdAt",
        "expiresAt"
       ],
       "additionalProperties": false
      }
     },
     "delegations": {
      "type": "object",
      "properties": {
       "active": {
        "type": "number"
       },
       "pending": {
        "type": "number"
       }
      },
      "required": [
       "active",
       "pending"
      ],
      "additionalProperties": false
     },
     "liveDelegatedSessions": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "agent": {
      "$ref": "#/components/schemas/AgentProfile"
     },
     "standing": {
      "$ref": "#/components/schemas/AgentStanding"
     },
     "sponsor": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "email": {
        "type": "string"
       },
       "status": {
        "type": "string",
        "enum": [
         "active",
         "disabled",
         "deleted"
        ]
       }
      },
      "required": [
       "id",
       "name",
       "status"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "keys",
     "delegations",
     "liveDelegatedSessions",
     "id",
     "tenantId",
     "name",
     "status",
     "createdAt",
     "agent",
     "standing"
    ],
    "additionalProperties": false
   },
   "SignedAgentCard": {
    "type": "object",
    "properties": {
     "card": {
      "allOf": [
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {}
       },
       {
        "type": "object",
        "properties": {
         "signatures": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "protected": {
             "type": "string"
            },
            "signature": {
             "type": "string"
            }
           },
           "required": [
            "protected",
            "signature"
           ],
           "additionalProperties": false
          }
         }
        },
        "required": [
         "signatures"
        ],
        "additionalProperties": false
       }
      ]
     },
     "attestation": {
      "$ref": "#/components/schemas/AgentAttestation"
     },
     "expiresAt": {
      "type": "number",
      "description": "Epoch milliseconds; re-sign before then."
     }
    },
    "required": [
     "card",
     "attestation",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "UpdateAgentInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "agentId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "sponsorId": {
      "type": "string"
     },
     "expiresAt": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "model": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "provider": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "purpose": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "url": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "protocols": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "delegable": {
      "type": "boolean"
     },
     "maxDelegatedSessionSeconds": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "boundary": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/PolicyDocument"
       }
      ]
     },
     "tokenAudiences": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     }
    },
    "required": [
     "tenantId",
     "agentId"
    ],
    "additionalProperties": false
   },
   "AgreementInput": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "content": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "required": {
      "type": "boolean"
     },
     "reacceptAfterDays": {
      "type": "number"
     }
    },
    "required": [
     "name",
     "content"
    ],
    "additionalProperties": false
   },
   "Agreement": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "content": {
      "type": "string",
      "description": "The text people accept (plain text or Markdown, at most 50 000 characters)."
     },
     "url": {
      "type": "string",
      "description": "Optional link to the canonical document."
     },
     "version": {
      "type": "number"
     },
     "required": {
      "type": "boolean",
      "description": "Required agreements count toward `principal.pendingAgreements` until accepted."
     },
     "reacceptAfterDays": {
      "type": "number",
      "description": "Acceptance lapses this many days after it was given (annual re-acceptance, for example)."
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "content",
     "version",
     "required",
     "createdAt",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "MyAgreement": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "content": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "version": {
      "type": "number"
     },
     "required": {
      "type": "boolean"
     },
     "accepted": {
      "type": "boolean",
      "description": "Accepted in the current version and not lapsed."
     },
     "acceptedAt": {
      "type": "number"
     },
     "acceptedVersion": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "name",
     "content",
     "version",
     "required",
     "accepted"
    ],
    "additionalProperties": false
   },
   "AgreementStatus": {
    "type": "object",
    "properties": {
     "agreement": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "version": {
        "type": "number"
       },
       "required": {
        "type": "boolean"
       }
      },
      "required": [
       "id",
       "name",
       "version",
       "required"
      ],
      "additionalProperties": false
     },
     "accepted": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "identity": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          }
         },
         "required": [
          "id",
          "name"
         ],
         "additionalProperties": false
        },
        "version": {
         "type": "number"
        },
        "acceptedAt": {
         "type": "number"
        }
       },
       "required": [
        "identity",
        "version",
        "acceptedAt"
       ],
       "additionalProperties": false
      }
     },
     "pending": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "identity": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          }
         },
         "required": [
          "id",
          "name"
         ],
         "additionalProperties": false
        },
        "acceptedVersion": {
         "type": "number"
        }
       },
       "required": [
        "identity"
       ],
       "additionalProperties": false
      },
      "description": "Active people without a current acceptance (outdated version, lapsed, or never accepted)."
     }
    },
    "required": [
     "agreement",
     "accepted",
     "pending"
    ],
    "additionalProperties": false
   },
   "AccessFinding": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string",
      "description": "Deterministic: the same condition on the same subject always yields the same ID, so it can be suppressed."
     },
     "kind": {
      "$ref": "#/components/schemas/FindingKind"
     },
     "severity": {
      "$ref": "#/components/schemas/FindingSeverity"
     },
     "title": {
      "type": "string"
     },
     "detail": {
      "type": "string"
     },
     "subject": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string",
        "enum": [
         "role",
         "policy",
         "identity",
         "group",
         "trust",
         "credential",
         "delegation",
         "team",
         "department"
        ]
       },
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "suppressed": {
      "type": "object",
      "properties": {
       "reason": {
        "type": "string"
       },
       "by": {
        "type": "string"
       },
       "at": {
        "type": "number"
       }
      },
      "required": [
       "reason",
       "by",
       "at"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "id",
     "kind",
     "severity",
     "title",
     "detail",
     "subject"
    ],
    "additionalProperties": false
   },
   "FindingKind": {
    "type": "string",
    "enum": [
     "unrestricted-admin-policy",
     "broad-action-wildcard",
     "admin-without-mfa",
     "separation-of-duties",
     "service-account-admin",
     "dormant-access",
     "stale-api-key",
     "trust-without-mfa",
     "trust-passes-foreign-attributes",
     "unattached-policy",
     "unused-role",
     "empty-role",
     "empty-group-with-access",
     "standing-privileged-access",
     "unused-eligible-binding",
     "orphaned-manager",
     "manager-cycle",
     "policy-lint",
     "agent-without-sponsor",
     "agent-admin",
     "unbounded-agent",
     "broad-delegation",
     "unused-delegation",
     "open-handoff",
     "agent-denials",
     "team-maintainers-grant-admin",
     "team-without-maintainer",
     "department-without-head"
    ]
   },
   "FindingSeverity": {
    "type": "string",
    "enum": [
     "high",
     "medium",
     "low"
    ]
   },
   "PolicyLintResult": {
    "type": "object",
    "properties": {
     "valid": {
      "type": "boolean"
     },
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       }
      },
      "required": [
       "code",
       "message"
      ],
      "additionalProperties": false,
      "description": "Why the document was rejected; warnings are empty when validation fails."
     },
     "warnings": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PolicyLintWarning"
      },
      "description": "Sorted by statement index, then code."
     }
    },
    "required": [
     "valid",
     "warnings"
    ],
    "additionalProperties": false
   },
   "PolicyLintWarning": {
    "type": "object",
    "properties": {
     "code": {
      "type": "string"
     },
     "severity": {
      "type": "string",
      "enum": [
       "warning",
       "info"
      ],
      "description": "`warning`: the document likely does not do what it says; `info`: worth a look, often intended."
     },
     "statement": {
      "type": "number"
     },
     "sid": {
      "type": "string"
     },
     "message": {
      "type": "string"
     }
    },
    "required": [
     "code",
     "severity",
     "statement",
     "message"
    ],
    "additionalProperties": false
   },
   "AssertionClaims": {
    "type": "object",
    "properties": {
     "iss": {
      "type": "string"
     },
     "sub": {
      "type": "string"
     },
     "aud": {
      "type": "string"
     },
     "iat": {
      "type": "number"
     },
     "exp": {
      "type": "number"
     },
     "jti": {
      "type": "string"
     },
     "tid": {
      "type": "string",
      "description": "The tenant the assertion was issued for."
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token"
      ],
      "description": "The caller's session kind; `session-token` for temporary credentials minted by `sts.getSessionToken`."
     },
     "mfa": {
      "type": "boolean"
     },
     "method": {
      "type": "string"
     },
     "impersonatorId": {
      "type": "string",
      "description": "Present when an administrator is acting through an impersonation session; `sub` is the member."
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "roles": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "groups": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "ext": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      },
      "description": "Caller-supplied public claims, at most 4 KiB of JSON."
     }
    },
    "required": [
     "iss",
     "sub",
     "aud",
     "iat",
     "exp",
     "jti",
     "tid",
     "kind",
     "mfa",
     "name",
     "roles",
     "groups"
    ],
    "additionalProperties": false
   },
   "AuditChainFailure": {
    "type": "string",
    "enum": [
     "sequence-gap",
     "previous-hash-mismatch",
     "hash-mismatch",
     "head-mismatch"
    ]
   },
   "MfaCredential": {
    "anyOf": [
     {
      "$ref": "#/components/schemas/CredentialInput"
     },
     {
      "type": "object",
      "properties": {
       "tenantId": {
        "type": "string"
       },
       "challenge": {
        "type": "string"
       }
      },
      "required": [
       "tenantId",
       "challenge"
      ],
      "additionalProperties": false
     }
    ]
   },
   "SessionResult": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string",
      "description": "The bearer token, returned once; only its hash is stored."
     },
     "session": {
      "$ref": "#/components/schemas/SafeSession"
     }
    },
    "required": [
     "token",
     "session"
    ],
    "additionalProperties": false
   },
   "SafeSession": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "lastSeenAt": {
      "type": "number"
     },
     "authenticatedAt": {
      "type": "number"
     },
     "mfa": {
      "type": "boolean"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token",
       "delegated"
      ],
      "description": "`user` (sign-in and impersonation sessions), `api-key` (service-account and agent keys), `role` (assumed roles,\nincluding web-identity sessions), `session-token` (temporary credentials minted from a user session or API key) or\n`delegated` (an agent acting on a person's behalf under a delegation; the identity is the person). Code that\nbranches on the kind must fail closed for kinds it does not know."
     },
     "agentId": {
      "type": "string",
      "description": "Delegated sessions: the agent acting for the session's identity, and the delegation that allows it."
     },
     "delegationId": {
      "type": "string"
     },
     "mfaAuthenticatedAt": {
      "type": "number",
      "description": "When a first-hand second factor (TOTP, passkey or an MFA step-up) was last verified for this session; exposed to\npolicies as principal.mfaTime. Absent on remembered-device, impersonation, API-key, web-identity and legacy rows."
     },
     "credentialAuthorityId": {
      "type": "string",
      "description": "The authority that issued an API key; session tokens minted from the key copy it and are bounded by its ceilings."
     },
     "sessionName": {
      "type": "string",
      "description": "Temporary credentials: a caller-chosen label (/^[\\w+=,.@-]{2,64}$/), exposed as principal.sessionName."
     },
     "sourceIdentity": {
      "type": "string",
      "description": "Role sessions: the verified or trust-permitted source identity, exposed as principal.sourceIdentity."
     },
     "sessionTags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "Role sessions: per-session tags admitted by the trust, exposed as principal.sessionTags.{key}."
     },
     "sourcePolicy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt"
      ],
      "description": "Temporary credentials issued as IAM-signed session JWTs; the row stores the hash of the JWT like any token."
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs: the audiences the token was issued for."
     },
     "webIdentity": {
      "type": "object",
      "properties": {
       "providerId": {
        "type": "string"
       },
       "issuer": {
        "type": "string"
       },
       "subject": {
        "type": "string"
       }
      },
      "required": [
       "providerId",
       "issuer",
       "subject"
      ],
      "additionalProperties": false,
      "description": "Web-identity role sessions: the OIDC provider and the verified external subject behind the session."
     },
     "method": {
      "type": "string",
      "enum": [
       "password",
       "passwordless-email",
       "passwordless-sms",
       "passkey",
       "federated",
       "impersonation"
      ],
      "description": "The sign-in method that established a user session; exposed to policies as principal.authMethod."
     },
     "client": {
      "$ref": "#/components/schemas/SessionClientInfo"
     },
     "name": {
      "type": "string",
      "description": "API keys only: an administrator-facing label and purpose (`credentials.create`/`credentials.update`)."
     },
     "description": {
      "type": "string"
     },
     "impersonatorId": {
      "type": "string",
      "description": "The administrator acting through this session (`identities.impersonate`); such sessions end with the administrator's own."
     },
     "impersonatorSessionId": {
      "type": "string"
     },
     "trustedDeviceId": {
      "type": "string",
      "description": "Set when a remembered device satisfied the MFA requirement instead of a fresh second factor."
     },
     "originalIdentityId": {
      "type": "string"
     },
     "sourceTenantId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "sourceSessionId": {
      "type": "string"
     },
     "sourceAuthorityIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "previousSignIn": {
      "$ref": "#/components/schemas/SignInRecord"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     }
    },
    "required": [
     "identityId",
     "createdAt",
     "expiresAt",
     "lastSeenAt",
     "authenticatedAt",
     "mfa",
     "kind",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "SessionClientInfo": {
    "type": "object",
    "properties": {
     "ip": {
      "type": "string"
     },
     "userAgent": {
      "type": "string"
     },
     "label": {
      "type": "string",
      "description": "A short free-form label the application derives, such as a device name."
     }
    },
    "additionalProperties": false
   },
   "SignInRecord": {
    "type": "object",
    "properties": {
     "lastAt": {
      "type": "number",
      "description": "When the most recent session was issued by a sign-in flow, and from which client."
     },
     "lastClient": {
      "$ref": "#/components/schemas/SessionClientInfo"
     },
     "failedAttempts": {
      "type": "number",
      "description": "Attempts since `lastAt` that named this account with a wrong password, factor, or recovery code."
     },
     "lastFailedAt": {
      "type": "number"
     },
     "lastFailedClient": {
      "$ref": "#/components/schemas/SessionClientInfo"
     }
    },
    "required": [
     "failedAttempts"
    ],
    "additionalProperties": false
   },
   "MfaSessionResult": {
    "allOf": [
     {
      "$ref": "#/components/schemas/SessionResult"
     },
     {
      "type": "object",
      "properties": {
       "deviceToken": {
        "type": "string"
       },
       "deviceExpiresAt": {
        "type": "number"
       }
      },
      "additionalProperties": false
     }
    ]
   },
   "SignInResult": {
    "anyOf": [
     {
      "$ref": "#/components/schemas/SessionResult"
     },
     {
      "$ref": "#/components/schemas/MfaRequired"
     }
    ]
   },
   "MfaRequired": {
    "type": "object",
    "properties": {
     "mfaRequired": {
      "type": "boolean",
      "const": true
     },
     "challenge": {
      "type": "string"
     },
     "enrollmentRequired": {
      "type": "boolean",
      "description": "No authenticator is enrolled: enroll one (`beginMfa` / `confirmMfa`) or, when offered, use an emailed code."
     },
     "emailCodeAvailable": {
      "type": "boolean",
      "description": "`requestMfaCode` may email a one-time code for this challenge instead of enrolling an authenticator."
     },
     "passkeyAvailable": {
      "type": "boolean",
      "description": "A registered passkey may satisfy this challenge (`beginPasskeyMfa` / `finishPasskeyMfa`)."
     }
    },
    "required": [
     "mfaRequired",
     "challenge",
     "enrollmentRequired"
    ],
    "additionalProperties": false
   },
   "SafeIdentity": {
    "type": "object",
    "properties": {
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ],
      "description": "`user` (a person), `service` (a service account) or `agent` (an AI agent with a sponsor, see `agent`)."
     },
     "agent": {
      "$ref": "#/components/schemas/AgentProfile"
     },
     "email": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      },
      "description": "Typed attributes declared by `permissions.identityAttributes`, exposed to policies as principal.{name}."
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ],
      "description": "deleted identities are tombstones: no credentials, factors, bindings, or email, kept so audit records stay resolvable."
     },
     "emailVerified": {
      "type": "boolean"
     },
     "rootAdmin": {
      "type": "boolean"
     },
     "owner": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     },
     "passwordChangedAt": {
      "type": "number",
      "description": "When the current password was set; drives `passwordMaxAgeDays` (identities without it use `createdAt`)."
     },
     "phone": {
      "type": "string"
     },
     "phoneVerified": {
      "type": "boolean"
     },
     "expiresAt": {
      "type": "number",
      "description": "Scheduled deactivation (epoch milliseconds) for contractors and temporary service accounts: past this time the\nidentity's credentials are refused and the retention worker disables it (`identity:expire`)."
     },
     "managerId": {
      "type": "string",
      "description": "The person's manager within the tenant: approvals may route to them, and offboarding hands reports to a successor."
     },
     "deletedAt": {
      "type": "number"
     },
     "deletedEmail": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "kind",
     "name",
     "status",
     "emailVerified",
     "rootAdmin",
     "owner",
     "createdAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "SafePasskey": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "credentialId": {
      "type": "string"
     },
     "transports": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AuthenticatorTransportFuture"
      }
     },
     "name": {
      "type": "string",
      "description": "A label the person chose or the default derived from the authenticator's transports."
     },
     "createdAt": {
      "type": "number"
     },
     "lastUsedAt": {
      "type": "number",
      "description": "Last successful sign-in or MFA assertion with this passkey."
     },
     "deviceType": {
      "type": "string",
      "enum": [
       "singleDevice",
       "multiDevice"
      ],
      "description": "Whether the credential is bound to one authenticator or synced across devices (from the registration)."
     },
     "backedUp": {
      "type": "boolean"
     },
     "aaguid": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     }
    },
    "required": [
     "identityId",
     "credentialId",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AuthenticatorTransportFuture": {
    "type": "string",
    "enum": [
     "ble",
     "cable",
     "hybrid",
     "internal",
     "nfc",
     "smart-card",
     "usb"
    ]
   },
   "SafeTrustedDevice": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "lastUsedAt": {
      "type": "number"
     },
     "client": {
      "$ref": "#/components/schemas/SessionClientInfo"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     }
    },
    "required": [
     "identityId",
     "createdAt",
     "expiresAt",
     "lastUsedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "GrantAuthority": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "ceiling": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "parentAuthorityId": {
      "type": "string"
     },
     "revoked": {
      "type": "boolean"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "identityId",
     "ceiling",
     "revoked",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AccountOverview": {
    "type": "object",
    "properties": {
     "accountId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "hasProfile": {
      "type": "boolean"
     },
     "monthToDateMicros": {
      "type": "number",
      "description": "Billable spend this period (meters defined above the account)."
     },
     "monthToDate": {
      "type": "number"
     },
     "totalMicros": {
      "type": "number",
      "description": "Including the account's own chargeback meters."
     },
     "forecastMicros": {
      "type": "number"
     },
     "creditsMicros": {
      "type": "number"
     },
     "outstandingMicros": {
      "type": "number",
      "description": "Still owed on finalized and uncollectible invoices."
     },
     "lastStatement": {
      "$ref": "#/components/schemas/StatementSummary"
     }
    },
    "required": [
     "accountId",
     "name",
     "hasProfile",
     "monthToDateMicros",
     "monthToDate",
     "totalMicros",
     "creditsMicros",
     "outstandingMicros"
    ],
    "additionalProperties": false
   },
   "StatementSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "tenantName": {
      "type": "string"
     },
     "number": {
      "type": "string"
     },
     "period": {
      "type": "string"
     },
     "status": {
      "$ref": "#/components/schemas/InvoiceStatus"
     },
     "currency": {
      "type": "string"
     },
     "subtotalMicros": {
      "type": "number"
     },
     "creditsMicros": {
      "type": "number"
     },
     "totalMicros": {
      "type": "number"
     },
     "total": {
      "type": "number"
     },
     "amountPaidMicros": {
      "type": "number",
      "description": "Payments received toward it."
     },
     "amountDueMicros": {
      "type": "number",
      "description": "Still owed: the total less payments and credit notes."
     },
     "amountDue": {
      "type": "number"
     },
     "creditNotesMicros": {
      "type": "number"
     },
     "billingReason": {
      "type": "string",
      "enum": [
       "period",
       "subscription",
       "manual"
      ]
     },
     "issuedAt": {
      "type": "number"
     },
     "dueAt": {
      "type": "number"
     },
     "paidAt": {
      "type": "number"
     },
     "overdue": {
      "type": "boolean",
      "description": "Finalized, past its due date, and not paid in full."
     }
    },
    "required": [
     "id",
     "tenantId",
     "number",
     "period",
     "status",
     "currency",
     "subtotalMicros",
     "creditsMicros",
     "totalMicros",
     "total",
     "amountPaidMicros",
     "amountDueMicros",
     "amountDue",
     "billingReason",
     "issuedAt",
     "dueAt",
     "overdue"
    ],
    "additionalProperties": false
   },
   "InvoiceStatus": {
    "type": "string",
    "enum": [
     "draft",
     "finalized",
     "paid",
     "void",
     "uncollectible"
    ]
   },
   "AnomalyOptions": {
    "type": "object",
    "properties": {
     "day": {
      "type": "string",
      "description": "The day to check, `YYYY-MM-DD` in the billing time zone (default yesterday)."
     },
     "baselineDays": {
      "type": "number",
      "description": "Days before it that make the baseline average (3 to 90, default 14)."
     },
     "factor": {
      "type": "number",
      "description": "How many times the baseline counts as a spike (1.1 to 1000, default 3)."
     },
     "minimum": {
      "type": "number",
      "description": "Smallest spend and increase worth reporting, in currency units (default 10)."
     }
    },
    "additionalProperties": false
   },
   "SpendAnomaly": {
    "type": "object",
    "properties": {
     "dimension": {
      "type": "string",
      "enum": [
       "identity",
       "team",
       "meter"
      ]
     },
     "key": {
      "type": "string"
     },
     "label": {
      "type": "string"
     },
     "day": {
      "type": "string"
     },
     "costMicros": {
      "type": "number"
     },
     "baselineMicros": {
      "type": "number",
      "description": "Average daily spend over the baseline days."
     },
     "factor": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ],
      "description": "`costMicros / baselineMicros`, rounded; null for new spending (no baseline)."
     }
    },
    "required": [
     "dimension",
     "key",
     "day",
     "costMicros",
     "baselineMicros",
     "factor"
    ],
    "additionalProperties": false
   },
   "SubscriptionView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "accountId": {
      "type": "string"
     },
     "accountName": {
      "type": "string"
     },
     "planId": {
      "type": "string"
     },
     "planKey": {
      "type": "string"
     },
     "planName": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "trialing",
       "ended"
      ]
     },
     "startedAt": {
      "type": "number"
     },
     "trialEndsAt": {
      "type": "number"
     },
     "endsAt": {
      "type": "number"
     },
     "cancelAtPeriodEnd": {
      "type": "boolean"
     },
     "canceledAt": {
      "type": "number"
     },
     "seats": {
      "type": "number"
     },
     "billedAdvance": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Months already billed in advance."
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "accountId",
     "planId",
     "planKey",
     "planName",
     "status",
     "startedAt",
     "cancelAtPeriodEnd",
     "seats",
     "billedAdvance",
     "createdAt",
     "createdBy"
    ],
    "additionalProperties": false
   },
   "InvoiceItemView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "accountId": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "quantity": {
      "type": "number"
     },
     "unitAmountMicros": {
      "type": "number"
     },
     "unitAmount": {
      "type": "number"
     },
     "amountMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number"
     },
     "period": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "invoiced"
      ]
     },
     "statementId": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "enum": [
       "manual",
       "proration"
      ]
     },
     "subscriptionId": {
      "type": "string"
     },
     "metadata": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      }
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "accountId",
     "description",
     "quantity",
     "unitAmountMicros",
     "unitAmount",
     "amountMicros",
     "amount",
     "status",
     "source",
     "createdAt",
     "createdBy"
    ],
    "additionalProperties": false
   },
   "SpendCheck": {
    "type": "object",
    "properties": {
     "allowed": {
      "type": "boolean"
     },
     "budgets": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/BudgetStatus"
      },
      "description": "Every enforced budget that covers the usage."
     },
     "blockedBy": {
      "$ref": "#/components/schemas/BudgetStatus"
     }
    },
    "required": [
     "allowed",
     "budgets"
    ],
    "additionalProperties": false
   },
   "BudgetStatus": {
    "type": "object",
    "properties": {
     "budgetId": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "subjectType": {
      "$ref": "#/components/schemas/BudgetSubjectType"
     },
     "subjectId": {
      "type": "string"
     },
     "subjectName": {
      "type": "string"
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriodKind"
     },
     "windowStart": {
      "type": "string",
      "description": "First and last billing period of the current window."
     },
     "windowEnd": {
      "type": "string"
     },
     "amountMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number"
     },
     "spentMicros": {
      "type": "number"
     },
     "spent": {
      "type": "number"
     },
     "percent": {
      "type": "number",
      "description": "Spent as a percent of the amount (one decimal)."
     },
     "forecastMicros": {
      "type": "number"
     },
     "forecastPercent": {
      "type": "number"
     },
     "thresholds": {
      "type": "array",
      "items": {
       "type": "number"
      }
     },
     "reached": {
      "type": "array",
      "items": {
       "type": "number"
      },
      "description": "Thresholds already reached in this window."
     },
     "exceeded": {
      "type": "boolean"
     },
     "enforce": {
      "type": "boolean"
     },
     "meters": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "budgetId",
     "tenantId",
     "name",
     "subjectType",
     "subjectId",
     "period",
     "windowStart",
     "windowEnd",
     "amountMicros",
     "amount",
     "spentMicros",
     "spent",
     "percent",
     "thresholds",
     "reached",
     "exceeded",
     "enforce"
    ],
    "additionalProperties": false
   },
   "BudgetSubjectType": {
    "type": "string",
    "enum": [
     "tenant",
     "identity",
     "team",
     "department"
    ]
   },
   "BudgetPeriodKind": {
    "type": "string",
    "enum": [
     "month",
     "quarter",
     "year"
    ]
   },
   "ClosePeriodResult": {
    "type": "object",
    "properties": {
     "period": {
      "type": "string"
     },
     "issued": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "accountId": {
         "type": "string"
        },
        "statementId": {
         "type": "string"
        },
        "number": {
         "type": "string"
        },
        "totalMicros": {
         "type": "number"
        },
        "recipients": {
         "type": "number"
        }
       },
       "required": [
        "accountId",
        "statementId",
        "number",
        "totalMicros",
        "recipients"
       ],
       "additionalProperties": false
      }
     },
     "drafted": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "accountId": {
         "type": "string"
        },
        "statementId": {
         "type": "string"
        },
        "totalMicros": {
         "type": "number"
        }
       },
       "required": [
        "accountId",
        "statementId",
        "totalMicros"
       ],
       "additionalProperties": false
      },
      "description": "Invoices kept (or refreshed) as drafts (`draft`, or `billing.autoFinalize: false`)."
     },
     "skipped": {
      "type": "object",
      "properties": {
       "existing": {
        "type": "number"
       },
       "empty": {
        "type": "number"
       }
      },
      "required": [
       "existing",
       "empty"
      ],
      "additionalProperties": false
     },
     "sweptUsage": {
      "type": "number",
      "description": "Raw usage events past their retention that were deleted."
     }
    },
    "required": [
     "period",
     "issued",
     "drafted",
     "skipped",
     "sweptUsage"
    ],
    "additionalProperties": false
   },
   "BillingBudgetInput": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "tenant",
       "identity",
       "team",
       "department"
      ]
     },
     "subjectId": {
      "type": "string",
      "description": "The tenant (default: the budget's own), team, department or identity."
     },
     "amount": {
      "type": "number",
      "description": "In currency units."
     },
     "period": {
      "type": "string",
      "enum": [
       "month",
       "quarter",
       "year"
      ]
     },
     "meters": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "thresholds": {
      "type": "array",
      "items": {
       "type": "number"
      }
     },
     "forecastAlerts": {
      "type": "boolean"
     },
     "notify": {
      "type": "object",
      "properties": {
       "owners": {
        "type": "boolean"
       },
       "subject": {
        "type": "boolean"
       },
       "emails": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      },
      "additionalProperties": false
     },
     "enforce": {
      "type": "boolean"
     }
    },
    "required": [
     "name",
     "amount"
    ],
    "additionalProperties": false
   },
   "BillingBudgetView": {
    "type": "object",
    "properties": {
     "forecastAlerts": {
      "type": "boolean"
     },
     "notify": {
      "type": "object",
      "properties": {
       "owners": {
        "type": "boolean"
       },
       "subject": {
        "type": "boolean"
       },
       "emails": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      },
      "required": [
       "owners",
       "subject",
       "emails"
      ],
      "additionalProperties": false
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "updatedAt": {
      "type": "number"
     },
     "budgetId": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "subjectType": {
      "$ref": "#/components/schemas/BudgetSubjectType"
     },
     "subjectId": {
      "type": "string"
     },
     "subjectName": {
      "type": "string"
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriodKind"
     },
     "windowStart": {
      "type": "string",
      "description": "First and last billing period of the current window."
     },
     "windowEnd": {
      "type": "string"
     },
     "amountMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number"
     },
     "spentMicros": {
      "type": "number"
     },
     "spent": {
      "type": "number"
     },
     "percent": {
      "type": "number",
      "description": "Spent as a percent of the amount (one decimal)."
     },
     "forecastMicros": {
      "type": "number"
     },
     "forecastPercent": {
      "type": "number"
     },
     "thresholds": {
      "type": "array",
      "items": {
       "type": "number"
      }
     },
     "reached": {
      "type": "array",
      "items": {
       "type": "number"
      },
      "description": "Thresholds already reached in this window."
     },
     "exceeded": {
      "type": "boolean"
     },
     "enforce": {
      "type": "boolean"
     },
     "meters": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "forecastAlerts",
     "notify",
     "createdAt",
     "createdBy",
     "updatedAt",
     "budgetId",
     "tenantId",
     "name",
     "subjectType",
     "subjectId",
     "period",
     "windowStart",
     "windowEnd",
     "amountMicros",
     "amount",
     "spentMicros",
     "spent",
     "percent",
     "thresholds",
     "reached",
     "exceeded",
     "enforce"
    ],
    "additionalProperties": false
   },
   "CouponView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "percentOff": {
      "type": "number"
     },
     "amountOffMicros": {
      "type": "number"
     },
     "amountOff": {
      "type": "number"
     },
     "duration": {
      "type": "string",
      "enum": [
       "once",
       "repeating",
       "forever"
      ]
     },
     "durationInMonths": {
      "type": "number"
     },
     "maxRedemptions": {
      "type": "number"
     },
     "redemptions": {
      "type": "number"
     },
     "redeemBy": {
      "type": "number"
     },
     "active": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "code",
     "name",
     "duration",
     "redemptions",
     "active",
     "createdAt"
    ],
    "additionalProperties": false
   },
   "CreditNoteView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "accountId": {
      "type": "string"
     },
     "number": {
      "type": "string"
     },
     "statementId": {
      "type": "string"
     },
     "statementNumber": {
      "type": "string"
     },
     "amountMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number"
     },
     "reason": {
      "$ref": "#/components/schemas/CreditNoteReason"
     },
     "memo": {
      "type": "string"
     },
     "applied": {
      "type": "object",
      "properties": {
       "dueMicros": {
        "type": "number"
       },
       "creditMicros": {
        "type": "number"
       },
       "refundMicros": {
        "type": "number"
       }
      },
      "required": [
       "dueMicros",
       "creditMicros",
       "refundMicros"
      ],
      "additionalProperties": false
     },
     "creditId": {
      "type": "string"
     },
     "issuedAt": {
      "type": "number"
     },
     "issuedBy": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "accountId",
     "number",
     "statementId",
     "statementNumber",
     "amountMicros",
     "amount",
     "reason",
     "applied",
     "issuedAt",
     "issuedBy"
    ],
    "additionalProperties": false
   },
   "CreditNoteReason": {
    "type": "string",
    "enum": [
     "duplicate",
     "fraudulent",
     "order_change",
     "product_unsatisfactory",
     "other"
    ]
   },
   "MeterView": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "unit": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "aggregation": {
      "type": "string",
      "enum": [
       "sum",
       "unique"
      ]
     },
     "pricing": {
      "type": "string",
      "enum": [
       "rate-card",
       "reported"
      ]
     },
     "archived": {
      "type": "boolean"
     },
     "definedBy": {
      "type": "string",
      "description": "The defining tenant."
     },
     "scope": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ],
      "description": "`platform` when the root tenant defines it."
     },
     "inherited": {
      "type": "boolean",
      "description": "Defined by an ancestor rather than this tenant."
     },
     "price": {
      "$ref": "#/components/schemas/PriceView"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "key",
     "name",
     "unit",
     "aggregation",
     "pricing",
     "archived",
     "definedBy",
     "scope",
     "inherited",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "PriceView": {
    "type": "object",
    "properties": {
     "meter": {
      "type": "string"
     },
     "targetTenantId": {
      "type": "string",
      "description": "The tenant (and subtree) the price applies to."
     },
     "targetTenantName": {
      "type": "string"
     },
     "definerId": {
      "type": "string",
      "description": "The tenant that defines the meter and set the price."
     },
     "effectiveFrom": {
      "type": "string"
     },
     "price": {
      "type": "object",
      "properties": {
       "maximumAmount": {
        "type": "number"
       },
       "minimumAmount": {
        "type": "number"
       },
       "includedQuantity": {
        "type": "number"
       },
       "packageAmount": {
        "type": "number"
       },
       "packageSize": {
        "type": "number"
       },
       "tiers": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "flatAmount": {
           "type": "number"
          },
          "upTo": {
           "anyOf": [
            {
             "type": "null"
            },
            {
             "type": "number"
            }
           ]
          },
          "unitAmount": {
           "type": "number"
          }
         },
         "required": [
          "upTo",
          "unitAmount"
         ],
         "additionalProperties": false
        }
       },
       "unitAmount": {
        "type": "number"
       },
       "model": {
        "$ref": "#/components/schemas/PriceModel"
       }
      },
      "required": [
       "model"
      ],
      "additionalProperties": false
     },
     "note": {
      "type": "string"
     },
     "setAt": {
      "type": "number"
     },
     "setBy": {
      "type": "string"
     }
    },
    "required": [
     "meter",
     "targetTenantId",
     "definerId",
     "effectiveFrom",
     "price",
     "setAt",
     "setBy"
    ],
    "additionalProperties": false
   },
   "PriceModel": {
    "type": "string",
    "enum": [
     "per-unit",
     "graduated",
     "volume",
     "package"
    ]
   },
   "PlanView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "key": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "items": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PlanItemView"
      }
     },
     "trialDays": {
      "type": "number"
     },
     "selfServe": {
      "type": "boolean"
     },
     "archived": {
      "type": "boolean"
     },
     "subscribers": {
      "type": "number",
      "description": "Live subscriptions (root administrators only)."
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "key",
     "name",
     "items",
     "selfServe",
     "archived",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "PlanItemView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "fee",
       "seat",
       "usage"
      ]
     },
     "name": {
      "type": "string"
     },
     "amount": {
      "type": "number",
      "description": "`fee`: the monthly amount."
     },
     "unitAmount": {
      "type": "number",
      "description": "`seat`: the monthly amount per seat."
     },
     "includedSeats": {
      "type": "number"
     },
     "billing": {
      "type": "string",
      "enum": [
       "advance",
       "arrears"
      ]
     },
     "meter": {
      "type": "string"
     },
     "price": {
      "type": "object",
      "properties": {
       "maximumAmount": {
        "type": "number"
       },
       "minimumAmount": {
        "type": "number"
       },
       "includedQuantity": {
        "type": "number"
       },
       "packageAmount": {
        "type": "number"
       },
       "packageSize": {
        "type": "number"
       },
       "tiers": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "flatAmount": {
           "type": "number"
          },
          "upTo": {
           "anyOf": [
            {
             "type": "null"
            },
            {
             "type": "number"
            }
           ]
          },
          "unitAmount": {
           "type": "number"
          }
         },
         "required": [
          "upTo",
          "unitAmount"
         ],
         "additionalProperties": false
        }
       },
       "unitAmount": {
        "type": "number"
       },
       "model": {
        "$ref": "#/components/schemas/PriceModel"
       }
      },
      "required": [
       "model"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "id",
     "kind",
     "name"
    ],
    "additionalProperties": false
   },
   "SpendReport": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "period": {
      "type": "string"
     },
     "periodStart": {
      "type": "number"
     },
     "periodEnd": {
      "type": "number"
     },
     "groupBy": {
      "$ref": "#/components/schemas/SpendGroupBy"
     },
     "rows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/SpendRow"
      }
     },
     "total": {
      "type": "object",
      "properties": {
       "costMicros": {
        "type": "number"
       },
       "amount": {
        "type": "number"
       },
       "events": {
        "type": "number"
       }
      },
      "required": [
       "costMicros",
       "amount",
       "events"
      ],
      "additionalProperties": false
     },
     "forecast": {
      "type": "object",
      "properties": {
       "costMicros": {
        "type": "number"
       },
       "amount": {
        "type": "number"
       }
      },
      "required": [
       "costMicros",
       "amount"
      ],
      "additionalProperties": false,
      "description": "For the current period: the total projected linearly to the end of the period."
     },
     "sharedMicros": {
      "type": "number",
      "description": "With `shareUnattributed`: the unattributed spend that was spread over the groups."
     }
    },
    "required": [
     "tenantId",
     "currency",
     "period",
     "periodStart",
     "periodEnd",
     "groupBy",
     "rows",
     "total"
    ],
    "additionalProperties": false
   },
   "SpendGroupBy": {
    "anyOf": [
     {
      "type": "string",
      "const": "tenant"
     },
     {
      "type": "string",
      "const": "agent"
     },
     {
      "type": "string",
      "const": "identity"
     },
     {
      "type": "string",
      "const": "team"
     },
     {
      "type": "string",
      "const": "department"
     },
     {
      "type": "string",
      "const": "meter"
     },
     {
      "type": "string",
      "const": "day"
     },
     {
      "type": "string"
     }
    ]
   },
   "SpendRow": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "label": {
      "type": "string"
     },
     "costMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number",
      "description": "`costMicros` in currency units, rounded to the cent."
     },
     "share": {
      "type": "number",
      "description": "Percent of the report's total (0-100, one decimal)."
     },
     "events": {
      "type": "number"
     },
     "quantities": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "number"
      },
      "description": "Quantity per meter key (a row may mix units)."
     },
     "sharedMicros": {
      "type": "number",
      "description": "With `shareUnattributed`: the part of `costMicros` that is this group's share of unattributed spend."
     }
    },
    "required": [
     "key",
     "costMicros",
     "amount",
     "share",
     "events",
     "quantities"
    ],
    "additionalProperties": false
   },
   "SpendQuery": {
    "type": "object",
    "properties": {
     "period": {
      "type": "string",
      "description": "`YYYY-MM`; the current period by default."
     },
     "groupBy": {
      "anyOf": [
       {
        "type": "string",
        "const": "tenant"
       },
       {
        "type": "string",
        "const": "agent"
       },
       {
        "type": "string",
        "const": "identity"
       },
       {
        "type": "string",
        "const": "team"
       },
       {
        "type": "string",
        "const": "department"
       },
       {
        "type": "string",
        "const": "meter"
       },
       {
        "type": "string",
        "const": "day"
       },
       {
        "type": "string"
       }
      ]
     },
     "shareUnattributed": {
      "type": "boolean",
      "description": "Grouped by identity, agent, team or department: spread the spend nobody in that dimension caused (shared\nservices, project-level usage) over the groups that did, in proportion to their spend (showback)."
     },
     "meter": {
      "type": "string",
      "description": "Only this meter key."
     },
     "identityId": {
      "type": "string",
      "description": "Only usage by this person, service account or agent."
     },
     "teamId": {
      "type": "string",
      "description": "Only usage attributed to this team (and the teams below it unless `rollUp` is false)."
     },
     "departmentId": {
      "type": "string",
      "description": "Only usage of people in this department (and those below it unless `rollUp` is false)."
     },
     "subTenantId": {
      "type": "string",
      "description": "Only usage in this tenant (a project) and the tenants below it."
     },
     "rollUp": {
      "type": "boolean",
      "description": "Include descendant teams and departments (default true)."
     },
     "billableOnly": {
      "type": "boolean",
      "description": "Only what statements bill: leaves out chargeback meters an account defines for itself."
     }
    },
    "additionalProperties": false
   },
   "CsvExport": {
    "type": "object",
    "properties": {
     "filename": {
      "type": "string"
     },
     "contentType": {
      "type": "string"
     },
     "body": {
      "type": "string"
     }
    },
    "required": [
     "filename",
     "contentType",
     "body"
    ],
    "additionalProperties": false
   },
   "ProfileView": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "account": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "inherited": {
        "type": "boolean"
       }
      },
      "required": [
       "id",
       "name",
       "inherited"
      ],
      "additionalProperties": false,
      "description": "Who pays for this tenant: itself (with a profile or as an organization) or an ancestor."
     },
     "profile": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/BillingProfileFields"
       }
      ]
     }
    },
    "required": [
     "tenantId",
     "account",
     "profile"
    ],
    "additionalProperties": false
   },
   "BillingProfileFields": {
    "type": "object",
    "properties": {
     "companyName": {
      "type": "string"
     },
     "billingEmails": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Statements and spend alerts go here (in addition to owners when empty)."
     },
     "taxId": {
      "type": "string"
     },
     "address": {
      "type": "string"
     },
     "purchaseOrder": {
      "type": "string"
     },
     "costCenter": {
      "type": "string",
      "description": "Printed on statements; defaults to nothing."
     },
     "paymentTermsDays": {
      "type": "number",
      "description": "Days after issue a statement is due (overrides `billing.paymentTermsDays`)."
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "updatedAt": {
      "type": "number"
     },
     "updatedBy": {
      "type": "string"
     }
    },
    "required": [
     "billingEmails",
     "createdAt",
     "createdBy",
     "updatedAt",
     "updatedBy"
    ],
    "additionalProperties": false
   },
   "InvoicePayment": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "amountMicros": {
      "type": "number"
     },
     "method": {
      "type": "string",
      "description": "How it was paid: `card`, `bank_transfer`, `check`, `manual`, ... (free text, 32 characters)."
     },
     "reference": {
      "type": "string"
     },
     "receivedAt": {
      "type": "number"
     },
     "recordedBy": {
      "type": "string"
     },
     "overpaymentMicros": {
      "type": "number",
      "description": "What the payment exceeded the amount due by, kept as account credit (`creditId`)."
     },
     "creditId": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "amountMicros",
     "method",
     "receivedAt",
     "recordedBy"
    ],
    "additionalProperties": false
   },
   "StatementLine": {
    "type": "object",
    "properties": {
     "kind": {
      "type": "string",
      "enum": [
       "fee",
       "seat",
       "usage",
       "item"
      ]
     },
     "meter": {
      "type": "string",
      "description": "Usage lines: the meter key."
     },
     "name": {
      "type": "string"
     },
     "unit": {
      "type": "string"
     },
     "quantity": {
      "type": "number"
     },
     "unitAmountMicros": {
      "type": "number",
      "description": "Price of one unit, for fee, seat and item lines and single-tier usage."
     },
     "amountMicros": {
      "type": "number"
     },
     "pricing": {
      "type": "string",
      "enum": [
       "rate-card",
       "reported"
      ],
      "description": "Usage lines: how the meter is priced."
     },
     "price": {
      "type": "object",
      "properties": {
       "model": {
        "$ref": "#/components/schemas/PriceModel"
       },
       "effectiveFrom": {
        "type": "string"
       },
       "source": {
        "type": "string",
        "enum": [
         "rate-card",
         "plan"
        ]
       }
      },
      "required": [
       "model",
       "effectiveFrom"
      ],
      "additionalProperties": false
     },
     "unpriced": {
      "type": "boolean"
     },
     "tiers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TierLine"
      },
      "description": "Tiers the quantity used (graduated and volume prices)."
     },
     "includedQuantity": {
      "type": "number",
      "description": "Free units taken off before pricing."
     },
     "adjustedFromMicros": {
      "type": "number",
      "description": "The price's minimum or maximum changed the amount; this is what the tiers came to."
     },
     "servicePeriod": {
      "type": "object",
      "properties": {
       "start": {
        "type": "number"
       },
       "end": {
        "type": "number"
       }
      },
      "required": [
       "start",
       "end"
      ],
      "additionalProperties": false,
      "description": "Fee and seat lines: the time they pay for."
     },
     "proration": {
      "type": "number",
      "description": "Share of the service period billed (0-1) when a subscription started, ended or changed within it."
     },
     "subscriptionId": {
      "type": "string"
     },
     "planItemId": {
      "type": "string"
     },
     "invoiceItemId": {
      "type": "string"
     },
     "description": {
      "type": "string"
     }
    },
    "required": [
     "name",
     "unit",
     "quantity",
     "amountMicros"
    ],
    "additionalProperties": false
   },
   "TierLine": {
    "type": "object",
    "properties": {
     "from": {
      "type": "number",
      "description": "First and last unit of the tier (`to` null: no upper bound)."
     },
     "to": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "quantity": {
      "type": "number"
     },
     "unitAmountMicros": {
      "type": "number"
     },
     "flatAmountMicros": {
      "type": "number"
     },
     "amountMicros": {
      "type": "number"
     }
    },
    "required": [
     "from",
     "to",
     "quantity",
     "unitAmountMicros",
     "amountMicros"
    ],
    "additionalProperties": false
   },
   "StatementAllocation": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "costMicros": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "name",
     "costMicros"
    ],
    "additionalProperties": false
   },
   "TermsView": {
    "type": "object",
    "properties": {
     "accountId": {
      "type": "string"
     },
     "inherited": {
      "type": "boolean",
      "description": "An ancestor pays for the tenant; its terms apply and are not shown."
     },
     "discountPercent": {
      "type": "number"
     },
     "minimumCommitmentMicros": {
      "type": "number"
     },
     "minimumCommitment": {
      "type": "number"
     },
     "taxRatePercent": {
      "type": "number"
     },
     "taxLabel": {
      "type": "string"
     },
     "setAt": {
      "type": "number"
     },
     "setBy": {
      "type": "string"
     }
    },
    "required": [
     "accountId",
     "inherited"
    ],
    "additionalProperties": false
   },
   "CreditView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "amountMicros": {
      "type": "number"
     },
     "amount": {
      "type": "number"
     },
     "remainingMicros": {
      "type": "number"
     },
     "remaining": {
      "type": "number"
     },
     "reason": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "grantedAt": {
      "type": "number"
     },
     "grantedBy": {
      "type": "string"
     },
     "revokedAt": {
      "type": "number"
     },
     "active": {
      "type": "boolean",
      "description": "Not revoked, not expired, and something left."
     }
    },
    "required": [
     "id",
     "tenantId",
     "amountMicros",
     "amount",
     "remainingMicros",
     "remaining",
     "reason",
     "grantedAt",
     "grantedBy",
     "active"
    ],
    "additionalProperties": false
   },
   "DiscountView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "accountId": {
      "type": "string"
     },
     "couponId": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "percentOff": {
      "type": "number"
     },
     "amountOffMicros": {
      "type": "number"
     },
     "amountOff": {
      "type": "number"
     },
     "duration": {
      "type": "string",
      "enum": [
       "once",
       "repeating",
       "forever"
      ]
     },
     "durationInMonths": {
      "type": "number"
     },
     "appliedInvoices": {
      "type": "number"
     },
     "redeemedAt": {
      "type": "number"
     },
     "redeemedBy": {
      "type": "string"
     },
     "endedAt": {
      "type": "number"
     },
     "active": {
      "type": "boolean",
      "description": "Discounts the invoice for the current month."
     }
    },
    "required": [
     "id",
     "accountId",
     "couponId",
     "code",
     "name",
     "duration",
     "appliedInvoices",
     "redeemedAt",
     "redeemedBy",
     "active"
    ],
    "additionalProperties": false
   },
   "StatementDraft": {
    "type": "object",
    "properties": {
     "accountId": {
      "type": "string"
     },
     "paymentTermsDays": {
      "type": "number"
     },
     "invoiceItemIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Pending invoice items the invoice bills."
     },
     "advanceBilled": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "subscriptionId": {
         "type": "string"
        },
        "period": {
         "type": "string"
        }
       },
       "required": [
        "subscriptionId",
        "period"
       ],
       "additionalProperties": false
      },
      "description": "Subscription months it bills in advance."
     },
     "carryForwardMicros": {
      "type": "number",
      "description": "A negative balance (credit items above the charges) that becomes account credit when it is finalized."
     },
     "period": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "periodStart": {
      "type": "number"
     },
     "periodEnd": {
      "type": "number"
     },
     "lines": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/StatementLine"
      }
     },
     "billingReason": {
      "type": "string",
      "enum": [
       "period",
       "subscription",
       "manual"
      ],
      "description": "Why the invoice exists: the monthly close (default), a new subscription's first period, or an invoice made by hand."
     },
     "subtotalMicros": {
      "type": "number",
      "description": "The sum of the lines."
     },
     "coupons": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "discountId": {
         "type": "string"
        },
        "code": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "amountMicros": {
         "type": "number"
        }
       },
       "required": [
        "discountId",
        "code",
        "name",
        "amountMicros"
       ],
       "additionalProperties": false
      },
      "description": "Coupon discounts (`billing.redeemCoupon`), after the contract discount."
     },
     "discount": {
      "type": "object",
      "properties": {
       "percent": {
        "type": "number"
       },
       "amountMicros": {
        "type": "number"
       }
      },
      "required": [
       "percent",
       "amountMicros"
      ],
      "additionalProperties": false,
      "description": "The account's negotiated discount on usage (`billing.setTerms`)."
     },
     "commitment": {
      "type": "object",
      "properties": {
       "minimumMicros": {
        "type": "number"
       },
       "trueUpMicros": {
        "type": "number"
       }
      },
      "required": [
       "minimumMicros",
       "trueUpMicros"
      ],
      "additionalProperties": false,
      "description": "What the month fell short of the account's minimum monthly commitment, billed on top."
     },
     "creditsMicros": {
      "type": "number"
     },
     "creditsApplied": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "creditId": {
         "type": "string"
        },
        "amountMicros": {
         "type": "number"
        }
       },
       "required": [
        "creditId",
        "amountMicros"
       ],
       "additionalProperties": false
      }
     },
     "tax": {
      "type": "object",
      "properties": {
       "label": {
        "type": "string"
       },
       "ratePercent": {
        "type": "number"
       },
       "amountMicros": {
        "type": "number"
       }
      },
      "required": [
       "label",
       "ratePercent",
       "amountMicros"
      ],
      "additionalProperties": false,
      "description": "Tax on the amount due after discount, commitment and credit."
     },
     "totalMicros": {
      "type": "number"
     },
     "breakdown": {
      "type": "object",
      "properties": {
       "tenants": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/StatementAllocation"
        }
       },
       "teams": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/StatementAllocation"
        }
       },
       "departments": {
        "type": "array",
        "items": {
         "allOf": [
          {
           "$ref": "#/components/schemas/StatementAllocation"
          },
          {
           "type": "object",
           "properties": {
            "costCenter": {
             "type": "string"
            }
           },
           "additionalProperties": false
          }
         ]
        }
       },
       "identities": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/StatementAllocation"
        }
       }
      },
      "required": [
       "tenants",
       "teams",
       "departments",
       "identities"
      ],
      "additionalProperties": false,
      "description": "Where the billable spend came from (largest first; teams, departments and people capped at 50)."
     },
     "billTo": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "companyName": {
        "type": "string"
       },
       "taxId": {
        "type": "string"
       },
       "address": {
        "type": "string"
       },
       "purchaseOrder": {
        "type": "string"
       },
       "costCenter": {
        "type": "string"
       },
       "emails": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      },
      "required": [
       "name",
       "emails"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "accountId",
     "paymentTermsDays",
     "invoiceItemIds",
     "advanceBilled",
     "carryForwardMicros",
     "period",
     "currency",
     "periodStart",
     "periodEnd",
     "lines",
     "subtotalMicros",
     "creditsMicros",
     "creditsApplied",
     "totalMicros",
     "breakdown",
     "billTo"
    ],
    "additionalProperties": false
   },
   "UsageInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "meter": {
      "type": "string"
     },
     "quantity": {
      "type": "number",
      "description": "Default 1."
     },
     "cost": {
      "type": "number",
      "description": "Reported meters only: the event's cost in currency units."
     },
     "identityId": {
      "type": "string",
      "description": "The person, service account or agent that used it (an identity of the tenant or an ancestor)."
     },
     "teamId": {
      "type": "string",
      "description": "Attribute to this team instead of the person's own teams."
     },
     "tags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "Up to 10 labels (`project`, `environment`, `feature`) reports can group by."
     },
     "occurredAt": {
      "type": "number",
      "description": "When it happened (default now); at most five minutes ahead, and never in a period already invoiced."
     },
     "idempotencyKey": {
      "type": "string",
      "description": "Makes retries safe: a repeated key returns the first receipt and records nothing."
     }
    },
    "required": [
     "tenantId",
     "meter"
    ],
    "additionalProperties": false
   },
   "UsageReceipt": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "meter": {
      "type": "string"
     },
     "quantity": {
      "type": "number"
     },
     "costMicros": {
      "type": "number"
     },
     "period": {
      "type": "string"
     },
     "occurredAt": {
      "type": "number"
     },
     "identityId": {
      "type": "string"
     },
     "teamIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "departmentId": {
      "type": "string"
     },
     "duplicate": {
      "type": "boolean",
      "description": "True when the idempotency key had already been used."
     }
    },
    "required": [
     "id",
     "tenantId",
     "meter",
     "quantity",
     "period",
     "occurredAt",
     "teamIds",
     "duplicate"
    ],
    "additionalProperties": false
   },
   "InvoiceDocument": {
    "type": "object",
    "properties": {
     "filename": {
      "type": "string"
     },
     "contentType": {
      "type": "string"
     },
     "body": {
      "type": "string"
     }
    },
    "required": [
     "filename",
     "contentType",
     "body"
    ],
    "additionalProperties": false
   },
   "SpendFilters": {
    "type": "object",
    "properties": {
     "meter": {
      "type": "string",
      "description": "Only this meter key."
     },
     "identityId": {
      "type": "string",
      "description": "Only usage by this person, service account or agent."
     },
     "teamId": {
      "type": "string",
      "description": "Only usage attributed to this team (and the teams below it unless `rollUp` is false)."
     },
     "departmentId": {
      "type": "string",
      "description": "Only usage of people in this department (and those below it unless `rollUp` is false)."
     },
     "subTenantId": {
      "type": "string",
      "description": "Only usage in this tenant (a project) and the tenants below it."
     },
     "rollUp": {
      "type": "boolean",
      "description": "Include descendant teams and departments (default true)."
     },
     "billableOnly": {
      "type": "boolean",
      "description": "Only what statements bill: leaves out chargeback meters an account defines for itself."
     }
    },
    "additionalProperties": false
   },
   "SpendTrend": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "currency": {
      "type": "string"
     },
     "months": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "period": {
         "type": "string"
        },
        "costMicros": {
         "type": "number"
        },
        "amount": {
         "type": "number"
        }
       },
       "required": [
        "period",
        "costMicros",
        "amount"
       ],
       "additionalProperties": false
      }
     },
     "forecast": {
      "type": "object",
      "properties": {
       "costMicros": {
        "type": "number"
       },
       "amount": {
        "type": "number"
       }
      },
      "required": [
       "costMicros",
       "amount"
      ],
      "additionalProperties": false,
      "description": "Projection of the current period (the last month)."
     }
    },
    "required": [
     "tenantId",
     "currency",
     "months"
    ],
    "additionalProperties": false
   },
   "BindingInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string"
     },
     "authorityId": {
      "type": "string"
     },
     "startsAt": {
      "type": "number",
      "description": "Future-dated grant: the binding grants nothing before this time."
     },
     "expiresAt": {
      "type": "number"
     },
     "window": {
      "$ref": "#/components/schemas/AccessWindow"
     },
     "eligible": {
      "type": "boolean"
     },
     "maxActivationMs": {
      "type": "number"
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requireMfa": {
      "type": "boolean"
     },
     "requireApproval": {
      "type": "boolean",
      "description": "Activation starts as a request that an approver grants or denies."
     },
     "approverGroupId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "Group whose members approve requests (and are emailed them); null clears it."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager may approve and is emailed each request."
     }
    },
    "required": [
     "tenantId",
     "roleId",
     "subjectType",
     "subjectId"
    ],
    "additionalProperties": false
   },
   "AccessWindow": {
    "type": "object",
    "properties": {
     "days": {
      "type": "array",
      "items": {
       "type": "number"
      }
     },
     "from": {
      "type": "string"
     },
     "to": {
      "type": "string"
     },
     "timeZone": {
      "type": "string"
     }
    },
    "required": [
     "from",
     "to",
     "timeZone"
    ],
    "additionalProperties": false
   },
   "Binding": {
    "type": "object",
    "properties": {
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "authorityId": {
      "type": "string"
     },
     "startsAt": {
      "type": "number",
      "description": "Future-dated grant: the binding grants nothing before this time (onboarding that starts on a set day)."
     },
     "expiresAt": {
      "type": "number"
     },
     "accessRequestId": {
      "type": "string"
     },
     "eligible": {
      "type": "boolean",
      "description": "Just-in-time access: the role applies only while the subject holds a live activation."
     },
     "maxActivationMs": {
      "type": "number",
      "description": "Longest activation the subject may request (default one hour, at most seven days)."
     },
     "requireJustification": {
      "type": "boolean",
      "description": "Activation must state a justification, recorded in the audit trail."
     },
     "requireMfa": {
      "type": "boolean",
      "description": "Activation requires an MFA-verified session."
     },
     "requireApproval": {
      "type": "boolean",
      "description": "Activation starts as a request that an approver must grant (`bindings.approveActivation`)."
     },
     "approverGroupId": {
      "type": "string",
      "description": "When set, only members of this group may approve (besides root); they are emailed each request."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager (`Identity.managerId`) may approve and is emailed each request."
     },
     "window": {
      "$ref": "#/components/schemas/AccessWindow"
     },
     "packageAssignmentId": {
      "type": "string",
      "description": "Set when an access-package assignment created this binding; revoking the assignment removes it."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "subjectType",
     "subjectId",
     "roleId",
     "authorityId",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "EffectiveBinding": {
    "allOf": [
     {
      "$ref": "#/components/schemas/Binding"
     },
     {
      "type": "object",
      "properties": {
       "role": {
        "$ref": "#/components/schemas/Role"
       },
       "via": {
        "anyOf": [
         {
          "type": "string",
          "const": "identity"
         },
         {
          "type": "object",
          "properties": {
           "groupId": {
            "type": "string"
           }
          },
          "required": [
           "groupId"
          ],
          "additionalProperties": false
         }
        ]
       },
       "activation": {
        "type": "object",
        "properties": {
         "expiresAt": {
          "type": "number"
         },
         "id": {
          "type": "string"
         },
         "activatedAt": {
          "type": "number"
         }
        },
        "required": [
         "expiresAt",
         "id",
         "activatedAt"
        ],
        "additionalProperties": false
       },
       "pendingActivation": {
        "type": "object",
        "properties": {
         "id": {
          "type": "string"
         },
         "requestedAt": {
          "type": "number"
         },
         "expiresAt": {
          "type": "number"
         }
        },
        "required": [
         "id",
         "requestedAt",
         "expiresAt"
        ],
        "additionalProperties": false,
        "description": "An activation request awaiting approval (`expiresAt` is when the request lapses)."
       },
       "inWindow": {
        "type": "boolean",
        "description": "Present for bindings with an access window: whether the window is open right now."
       }
      },
      "required": [
       "via"
      ],
      "additionalProperties": false
     }
    ]
   },
   "Role": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "policyIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "protected": {
      "type": "boolean"
     },
     "inherits": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Roles whose grants this role includes (role hierarchy). Inherited grants stay bounded by this role's own\nauthority ceilings as well as the inherited role's, so inheriting a broader role cannot widen access."
     },
     "sessionsRevokedBefore": {
      "type": "number",
      "description": "Role sessions (classic and web identity) created before this time (epoch milliseconds) are refused\n(`roles.revokeSessions`). Monotonic and never in the future; role updates and config sync keep it."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "policyIds",
     "protected",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "EligibilityInput": {
    "type": "object",
    "properties": {
     "eligible": {
      "type": "boolean"
     },
     "maxActivationMs": {
      "type": "number"
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requireMfa": {
      "type": "boolean"
     },
     "requireApproval": {
      "type": "boolean",
      "description": "Activation starts as a request that an approver grants or denies."
     },
     "approverGroupId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "Group whose members approve requests (and are emailed them); null clears it."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager may approve and is emailed each request."
     }
    },
    "additionalProperties": false
   },
   "CertificationCampaign": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "open",
       "closed"
      ]
     },
     "createdBy": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "dueAt": {
      "type": "number"
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Only bindings of these roles are reviewed; unset reviews every non-protected role."
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "reviewerIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Identities allowed to decide; empty lets anyone holding `iam:certifications:review` decide."
     },
     "reviewerMode": {
      "type": "string",
      "enum": [
       "named",
       "manager"
      ],
      "description": "`manager` assigns each person's items to their manager (`CertificationItem.reviewerId`); items without one fall\nback to `reviewerIds`. Absent on campaigns created before reviewer modes, which are `named`."
     },
     "autoClose": {
      "type": "boolean",
      "description": "The deployment worker (`closeOverdueCertifications`) closes the campaign once `dueAt` has passed."
     },
     "undecided": {
      "$ref": "#/components/schemas/CertificationDecision"
     },
     "closedAt": {
      "type": "number"
     },
     "closedBy": {
      "type": "string",
      "description": "Who closed the campaign; `deployment-operator` when the worker closed it."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "status",
     "createdBy",
     "createdAt",
     "reviewerIds",
     "undecided",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "CertificationDecision": {
    "type": "string",
    "enum": [
     "keep",
     "revoke"
    ]
   },
   "CertificationProgress": {
    "type": "object",
    "properties": {
     "total": {
      "type": "number"
     },
     "decided": {
      "type": "number"
     },
     "keep": {
      "type": "number"
     },
     "revoke": {
      "type": "number"
     }
    },
    "required": [
     "total",
     "decided",
     "keep",
     "revoke"
    ],
    "additionalProperties": false
   },
   "CertificationDecisionInput": {
    "type": "object",
    "properties": {
     "itemId": {
      "type": "string"
     },
     "decision": {
      "$ref": "#/components/schemas/CertificationDecision"
     },
     "note": {
      "type": "string"
     }
    },
    "required": [
     "itemId",
     "decision"
    ],
    "additionalProperties": false
   },
   "CertificationItem": {
    "type": "object",
    "properties": {
     "campaignId": {
      "type": "string"
     },
     "bindingId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "roleName": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string"
     },
     "subjectName": {
      "type": "string"
     },
     "bindingExpiresAt": {
      "type": "number"
     },
     "eligible": {
      "type": "boolean"
     },
     "reviewerId": {
      "type": "string",
      "description": "Manager-mode campaigns: the person's manager, who reviews this item (administrators may still decide it)."
     },
     "decision": {
      "type": "string",
      "enum": [
       "keep",
       "revoke"
      ]
     },
     "decidedBy": {
      "type": "string"
     },
     "decidedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     },
     "outcome": {
      "type": "string",
      "enum": [
       "kept",
       "revoked",
       "already-removed",
       "revocation-failed"
      ]
     },
     "outcomeDetail": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "campaignId",
     "bindingId",
     "roleId",
     "roleName",
     "subjectType",
     "subjectId",
     "subjectName",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "ConfigChange": {
    "type": "object",
    "properties": {
     "kind": {
      "$ref": "#/components/schemas/ConfigChangeKind"
     },
     "name": {
      "type": "string",
      "description": "The item's name; for bindings, `{group} -> {role}`."
     },
     "action": {
      "$ref": "#/components/schemas/ConfigChangeAction"
     },
     "fields": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The properties that differ, for updates."
     },
     "before": {},
     "after": {}
    },
    "required": [
     "kind",
     "name",
     "action"
    ],
    "additionalProperties": false
   },
   "ConfigChangeKind": {
    "type": "string",
    "enum": [
     "role",
     "agent",
     "policy",
     "group",
     "team",
     "department",
     "package",
     "resourceType",
     "binding",
     "accessPolicy",
     "invariant",
     "agreement",
     "inferenceModel",
     "inferenceBudget"
    ]
   },
   "ConfigChangeAction": {
    "type": "string",
    "enum": [
     "create",
     "update",
     "delete",
     "unchanged"
    ]
   },
   "TenantConfig": {
    "type": "object",
    "properties": {
     "version": {
      "type": "number",
      "const": 1
     },
     "resourceTypes": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigResourceType"
      },
      "description": "Tenant-defined resource types (only with `permissions.mode: 'tenant-defined'`); `actions` lists verbs."
     },
     "policies": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigPolicy"
      }
     },
     "roles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigRole"
      }
     },
     "groups": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigGroup"
      }
     },
     "bindings": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigBinding"
      },
      "description": "Group role bindings; identity bindings are assigned at runtime and never synced."
     },
     "accessPolicy": {
      "$ref": "#/components/schemas/TenantAccessPolicy"
     },
     "packages": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigPackage"
      },
      "description": "Access packages naming their roles, groups, and optional birthright rule; who holds them is runtime state and never synced."
     },
     "invariants": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigInvariant"
      },
      "description": "Access invariants (guardrails), naming groups and people by group name and email."
     },
     "agreements": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigAgreement"
      },
      "description": "Terms of use; a content change publishes a new version that everyone accepts again."
     },
     "teams": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigTeam"
      },
      "description": "Teams by slug, with maintainers and members by email and the roles they hold by name (org-sync.ts)."
     },
     "departments": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigDepartment"
      },
      "description": "Departments by name, with heads and people by email (org-sync.ts)."
     },
     "agents": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigAgent"
      },
      "description": "AI agents by name, with their sponsor by email; keys and delegations are runtime state (ai-sync.ts)."
     },
     "inferenceModels": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigInferenceModel"
      },
      "description": "The tenant's own AI models, with their provider by name (ai-sync.ts; needs the `inference` option)."
     },
     "inferenceBudgets": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TenantConfigInferenceBudget"
      },
      "description": "Inference budgets, naming groups, people and agents (ai-sync.ts; needs the `inference` option)."
     }
    },
    "required": [
     "version"
    ],
    "additionalProperties": false
   },
   "TenantConfigResourceType": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "actions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "$ref": "#/components/schemas/AttributeType"
      }
     },
     "parent": {
      "type": "string"
     },
     "relations": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "name"
    ],
    "additionalProperties": false
   },
   "AttributeType": {
    "type": "string",
    "enum": [
     "string",
     "number",
     "boolean"
    ]
   },
   "TenantConfigPolicy": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     }
    },
    "required": [
     "name",
     "document"
    ],
    "additionalProperties": false
   },
   "TenantConfigRole": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "policies": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Names of attached policies from the same configuration or already in the tenant."
     },
     "permissions": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "A plain permissions list (an inline allow over every resource) or a full inline document, not both."
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "inherits": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Names of roles this role inherits, from the same configuration or already in the tenant."
     }
    },
    "required": [
     "name"
    ],
    "additionalProperties": false
   },
   "TenantConfigGroup": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "members": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Member emails; when present, membership is made to match exactly."
     }
    },
    "required": [
     "name"
    ],
    "additionalProperties": false
   },
   "TenantConfigBinding": {
    "type": "object",
    "properties": {
     "group": {
      "type": "string"
     },
     "role": {
      "type": "string"
     },
     "eligible": {
      "type": "boolean"
     },
     "maxActivationMs": {
      "type": "number"
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requireMfa": {
      "type": "boolean"
     },
     "requireApproval": {
      "type": "boolean"
     },
     "approverGroup": {
      "type": "string",
      "description": "Name of the group whose members approve activation requests."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager may approve activation requests."
     },
     "window": {
      "$ref": "#/components/schemas/AccessWindow"
     }
    },
    "required": [
     "group",
     "role"
    ],
    "additionalProperties": false
   },
   "TenantAccessPolicy": {
    "type": "object",
    "properties": {
     "maxActivationMs": {
      "type": "number",
      "description": "Caps every binding's `maxActivationMs` (one minute to seven days)."
     },
     "requireJustification": {
      "type": "boolean",
      "description": "Every activation must state a justification."
     },
     "requireMfa": {
      "type": "boolean",
      "description": "Every activation needs an MFA-verified session."
     },
     "requireApproval": {
      "type": "boolean",
      "description": "Every activation starts as an approval request."
     },
     "approvalLifetimeMs": {
      "type": "number",
      "description": "How long a request waits for a decision before it lapses (five minutes to thirty days; one day by default)."
     }
    },
    "additionalProperties": false
   },
   "TenantConfigPackage": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "roles": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Role names from the same configuration or already in the tenant."
     },
     "groups": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Group names from the same configuration or already in the tenant."
     },
     "maxDurationMs": {
      "type": "number"
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requestable": {
      "type": "boolean"
     },
     "approverGroup": {
      "type": "string",
      "description": "Name of the group whose members decide on requests."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager may decide on requests."
     },
     "autoAssign": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/TenantConfigAutoAssign"
       }
      ],
      "description": "Birthright rule; omitted = left as it is, null = removed. Owner and revision are runtime state and never synced."
     }
    },
    "required": [
     "name"
    ],
    "additionalProperties": false
   },
   "TenantConfigAutoAssign": {
    "type": "object",
    "properties": {
     "include": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {}
           }
          ]
         }
        }
       },
       "additionalProperties": false
      }
     },
     "exclude": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {},
           {},
           {},
           {},
           {}
          ]
         }
        }
       },
       "additionalProperties": false
      }
     },
     "graceMs": {
      "type": "number"
     },
     "maxGrants": {
      "type": "number"
     },
     "maxRemovals": {
      "type": "number"
     }
    },
    "required": [
     "include"
    ],
    "additionalProperties": false
   },
   "TenantConfigInvariant": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "subject": {
      "$ref": "#/components/schemas/TenantConfigInvariantSubject"
     },
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "expect": {
      "type": "string",
      "enum": [
       "allow",
       "deny"
      ]
     },
     "mode": {
      "type": "string",
      "enum": [
       "enforce",
       "monitor"
      ],
      "description": "Default `monitor`."
     },
     "assumeMfa": {
      "type": "boolean",
      "description": "Default true."
     }
    },
    "required": [
     "name",
     "subject",
     "action",
     "resource",
     "expect"
    ],
    "additionalProperties": false
   },
   "TenantConfigInvariantSubject": {
    "anyOf": [
     {
      "type": "object",
      "properties": {
       "everyone": {
        "type": "boolean",
        "const": true
       }
      },
      "required": [
       "everyone"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "group": {
        "type": "string"
       }
      },
      "required": [
       "group"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "identity": {
        "type": "string"
       }
      },
      "required": [
       "identity"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "attribute": {
        "type": "object",
        "properties": {
         "name": {
          "type": "string"
         },
         "value": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           }
          ]
         }
        },
        "required": [
         "name",
         "value"
        ],
        "additionalProperties": false
       }
      },
      "required": [
       "attribute"
      ],
      "additionalProperties": false
     }
    ]
   },
   "TenantConfigAgreement": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "content": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "required": {
      "type": "boolean",
      "description": "Default true."
     },
     "reacceptAfterDays": {
      "type": "number"
     }
    },
    "required": [
     "name",
     "content"
    ],
    "additionalProperties": false
   },
   "TenantConfigTeam": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string",
      "description": "The key teams are matched by; derived from the name when left out."
     },
     "description": {
      "type": "string"
     },
     "parent": {
      "type": "string",
      "description": "Slug of the parent team."
     },
     "department": {
      "type": "string",
      "description": "Name of the department the team belongs to."
     },
     "joinPolicy": {
      "type": "string",
      "enum": [
       "closed",
       "request"
      ],
      "description": "Default `closed`."
     },
     "memberManagement": {
      "type": "string",
      "enum": [
       "maintainers",
       "admins"
      ],
      "description": "Default `maintainers`."
     },
     "maintainers": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Maintainer emails. With `members`, the team's permanent direct members are made to match exactly."
     },
     "members": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Member emails (see `maintainers`)."
     },
     "roles": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Names of the roles the team holds as standing bindings; when present, made to match exactly."
     },
     "syncGroups": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Team sync: names of the groups whose members the team keeps (their synced members are runtime state)."
     }
    },
    "required": [
     "name",
     "slug"
    ],
    "additionalProperties": false
   },
   "TenantConfigDepartment": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parent": {
      "type": "string",
      "description": "Name of the parent department."
     },
     "head": {
      "type": "string",
      "description": "Email of the department head."
     },
     "costCenter": {
      "type": "string"
     },
     "members": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "People's emails; when present, the department's people are made to match exactly."
     }
    },
    "required": [
     "name"
    ],
    "additionalProperties": false
   },
   "TenantConfigAgent": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "sponsor": {
      "type": "string",
      "description": "The accountable person, by email (an active person of the tenant)."
     },
     "description": {
      "type": "string"
     },
     "purpose": {
      "type": "string"
     },
     "model": {
      "type": "string"
     },
     "provider": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "protocols": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "delegable": {
      "type": "boolean",
      "description": "Default true."
     },
     "maxDelegatedSessionSeconds": {
      "type": "number"
     },
     "boundary": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "tokenAudiences": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Services the agent may present delegations to (`delegations.issueToken`)."
     }
    },
    "required": [
     "name",
     "sponsor"
    ],
    "additionalProperties": false
   },
   "TenantConfigInferenceModel": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "provider": {
      "type": "string",
      "description": "A provider of this tenant or an ancestor, by name."
     },
     "upstreamModel": {
      "type": "string"
     },
     "displayName": {
      "type": "string"
     },
     "family": {
      "type": "string"
     },
     "tier": {
      "type": "string"
     },
     "contextWindow": {
      "type": "number"
     },
     "maxOutputTokens": {
      "type": "number"
     },
     "inputPricePerMTok": {
      "type": "number"
     },
     "outputPricePerMTok": {
      "type": "number"
     },
     "cachedInputPricePerMTok": {
      "type": "number"
     },
     "fallbacks": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Models the gateway tries, in order, when this one's provider fails."
     },
     "providerTools": {
      "type": "string",
      "enum": [
       "deny",
       "policy"
      ],
      "description": "Provider-run tools: `deny` or `policy` (`allow`, the default, is left out)."
     },
     "enabled": {
      "type": "boolean",
      "description": "Default true."
     }
    },
    "required": [
     "name",
     "provider",
     "upstreamModel"
    ],
    "additionalProperties": false
   },
   "TenantConfigInferenceBudget": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "subject": {
      "$ref": "#/components/schemas/TenantConfigBudgetSubject"
     },
     "scope": {
      "type": "string",
      "enum": [
       "shared",
       "each"
      ],
      "description": "Default `shared` (always shared for a person or an agent)."
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriod"
     },
     "maxTokens": {
      "type": "number"
     },
     "maxCostUsd": {
      "type": "number"
     },
     "maxRequests": {
      "type": "number"
     },
     "models": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "alertAtPercent": {
      "type": "number"
     }
    },
    "required": [
     "name",
     "subject",
     "period"
    ],
    "additionalProperties": false
   },
   "TenantConfigBudgetSubject": {
    "anyOf": [
     {
      "type": "string",
      "const": "tenant"
     },
     {
      "type": "object",
      "properties": {
       "group": {
        "type": "string"
       }
      },
      "required": [
       "group"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "identity": {
        "type": "string"
       }
      },
      "required": [
       "identity"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "agent": {
        "type": "string"
       }
      },
      "required": [
       "agent"
      ],
      "additionalProperties": false
     }
    ]
   },
   "BudgetPeriod": {
    "type": "string",
    "enum": [
     "month",
     "day",
     "minute",
     "hour"
    ]
   },
   "ConfigPlan": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "prune": {
      "type": "boolean"
     },
     "changes": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ConfigChange"
      }
     },
     "summary": {
      "type": "object",
      "properties": {
       "create": {
        "type": "number"
       },
       "update": {
        "type": "number"
       },
       "delete": {
        "type": "number"
       },
       "unchanged": {
        "type": "number"
       }
      },
      "required": [
       "create",
       "update",
       "delete",
       "unchanged"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "tenantId",
     "prune",
     "changes",
     "summary"
    ],
    "additionalProperties": false
   },
   "CredentialSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "lastUsedAt": {
      "type": "number",
      "description": "When the key last authenticated a request (recorded at most once a minute); absent until first use."
     },
     "expired": {
      "type": "boolean"
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "scopes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The action allowlist the key was issued with (`credentials.create({ scopes })`), when its policy is exactly that."
     },
     "credentialAuthorityId": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "identityId",
     "createdAt",
     "expiresAt",
     "expired"
    ],
    "additionalProperties": false
   },
   "DelegationScopeInput": {
    "type": "object",
    "properties": {
     "scopes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Actions the agent may take for the person (wildcards allowed); an alternative to `policy`."
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "confirm": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Action patterns the person confirms one call at a time (for example `billing:*`): refused with\n`CONFIRMATION_REQUIRED` until the person approves that action on that resource (`requestConfirmation`)."
     },
     "handoff": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "object",
        "properties": {
         "agents": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "depth": {
          "type": "number"
         }
        },
        "additionalProperties": false
       }
      ],
      "description": "Lets the agent hand parts of the delegation on to other agents (`handoff`): to any agent that accepts delegation or\nonly to `agents`, for `depth` hand-offs down the line (1 to 3; 1 by default). Off when absent; `null` in `approve`\nturns it off."
     },
     "spend": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "object",
        "properties": {
         "period": {
          "type": "string",
          "enum": [
           "month",
           "day",
           "minute",
           "hour"
          ]
         },
         "maxTokens": {
          "type": "number"
         },
         "maxCostUsd": {
          "type": "number"
         },
         "maxRequests": {
          "type": "number"
         }
        },
        "required": [
         "period"
        ],
        "additionalProperties": false
       }
      ],
      "description": "Caps what AI model calls made under the delegation (and hand-offs below it) may use per `period` (`minute`, `hour`,\n`day` or `month`): `maxTokens`, `maxCostUsd` and/or `maxRequests`. `null` in `approve` removes it."
     }
    },
    "additionalProperties": false
   },
   "DelegationSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "denied",
       "active",
       "revoked"
      ]
     },
     "expired": {
      "type": "boolean",
      "description": "Pending requests past their decision window and active delegations past their end read as expired."
     },
     "agent": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "model": {
        "type": "string"
       },
       "provider": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "subject": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "email": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "scopes": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "requestedBy": {
      "type": "string",
      "enum": [
       "agent",
       "subject",
       "handoff"
      ]
     },
     "reason": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "requestedSeconds": {
      "type": "number",
      "description": "Pending requests: how long the delegation will last once approved, in seconds."
     },
     "maxSessionSeconds": {
      "type": "number"
     },
     "decidedAt": {
      "type": "number"
     },
     "revokedAt": {
      "type": "number"
     },
     "revokedBy": {
      "type": "string"
     },
     "lastUsedAt": {
      "type": "number"
     },
     "confirm": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Actions the person confirms one call at a time."
     },
     "handoff": {
      "$ref": "#/components/schemas/DelegationHandoff"
     },
     "parentId": {
      "type": "string",
      "description": "A hand-off: the delegation it was handed on from."
     },
     "chain": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "name": {
         "type": "string"
        }
       },
       "required": [
        "id",
        "name"
       ],
       "additionalProperties": false
      },
      "description": "A hand-off: the agents above it, the person's own delegate first."
     },
     "spend": {
      "type": "object",
      "properties": {
       "period": {
        "type": "string",
        "enum": [
         "month",
         "day",
         "minute",
         "hour"
        ]
       },
       "maxTokens": {
        "type": "number"
       },
       "maxCostUsd": {
        "type": "number"
       },
       "maxRequests": {
        "type": "number"
       },
       "usedTokens": {
        "type": "number"
       },
       "usedCostUsd": {
        "type": "number"
       },
       "usedRequests": {
        "type": "number"
       },
       "resetsAt": {
        "type": "number"
       }
      },
      "required": [
       "period",
       "usedTokens",
       "usedCostUsd",
       "usedRequests",
       "resetsAt"
      ],
      "additionalProperties": false,
      "description": "The person's cap on AI model use under this delegation, and how much of it the current window has used."
     }
    },
    "required": [
     "id",
     "tenantId",
     "status",
     "expired",
     "agent",
     "subject",
     "policy",
     "requestedBy",
     "createdAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "DelegationHandoff": {
    "type": "object",
    "properties": {
     "agents": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The agents it may hand on to; any delegable agent of the tenant when absent."
     },
     "depth": {
      "type": "number",
      "description": "How many hand-offs may follow down the line, 1 to 3."
     }
    },
    "required": [
     "depth"
    ],
    "additionalProperties": false
   },
   "DelegatedCredential": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string"
     },
     "tokenType": {
      "type": "string",
      "const": "Bearer"
     },
     "expiresAt": {
      "type": "number"
     },
     "expiresIn": {
      "type": "number"
     },
     "session": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "kind": {
        "type": "string",
        "const": "delegated"
       },
       "tenantId": {
        "type": "string"
       },
       "identityId": {
        "type": "string",
        "description": "The person the agent acts for."
       },
       "agentId": {
        "type": "string"
       },
       "delegationId": {
        "type": "string"
       },
       "sessionName": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "kind",
       "tenantId",
       "identityId",
       "agentId",
       "delegationId"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "token",
     "tokenType",
     "expiresAt",
     "expiresIn",
     "session"
    ],
    "additionalProperties": false
   },
   "ConfirmationSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "delegationId": {
      "type": "string"
     },
     "agent": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "subjectId": {
      "type": "string"
     },
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "reason": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "approved",
       "rejected"
      ]
     },
     "expired": {
      "type": "boolean",
      "description": "Pending requests past their decision window and approvals past their validity read as expired."
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "validSeconds": {
      "type": "number"
     },
     "decidedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "tenantId",
     "delegationId",
     "agent",
     "subjectId",
     "action",
     "resource",
     "status",
     "expired",
     "createdAt",
     "expiresAt",
     "validSeconds"
    ],
    "additionalProperties": false
   },
   "DelegationToken": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string",
      "description": "The signed JWT (`typ` `biam-delegation+jwt`); send it to the service, for example as a bearer token."
     },
     "tokenType": {
      "type": "string",
      "const": "biam-delegation+jwt"
     },
     "tokenId": {
      "type": "string",
      "description": "The token's `jti`."
     },
     "issuer": {
      "type": "string"
     },
     "audience": {
      "type": "string"
     },
     "scopes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The scopes it carries (empty for a delegation given as a policy)."
     },
     "chain": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The agents from the person's own delegate to the one acting now."
     },
     "expiresAt": {
      "type": "number",
      "description": "Epoch milliseconds."
     },
     "expiresIn": {
      "type": "number"
     }
    },
    "required": [
     "token",
     "tokenType",
     "tokenId",
     "issuer",
     "audience",
     "scopes",
     "chain",
     "expiresAt",
     "expiresIn"
    ],
    "additionalProperties": false
   },
   "DepartmentInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "headId": {
      "type": "string"
     },
     "costCenter": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "DepartmentDetail": {
    "type": "object",
    "properties": {
     "path": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/DepartmentRef"
      },
      "description": "Departments above it, top first."
     },
     "children": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/DepartmentRef"
        },
        {
         "type": "object",
         "properties": {
          "memberCount": {
           "type": "number"
          },
          "totalMemberCount": {
           "type": "number"
          }
         },
         "required": [
          "memberCount",
          "totalMemberCount"
         ],
         "additionalProperties": false
        }
       ]
      }
     },
     "head": {
      "$ref": "#/components/schemas/DepartmentPerson"
     },
     "teams": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "slug": {
         "type": "string"
        }
       },
       "required": [
        "id",
        "name",
        "slug"
       ],
       "additionalProperties": false
      }
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "headId": {
      "type": "string"
     },
     "costCenter": {
      "type": "string"
     },
     "memberCount": {
      "type": "number",
      "description": "People in this department itself."
     },
     "totalMemberCount": {
      "type": "number",
      "description": "People in it and in every department below it."
     },
     "childCount": {
      "type": "number"
     },
     "teamCount": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "path",
     "children",
     "teams",
     "id",
     "tenantId",
     "name",
     "memberCount",
     "totalMemberCount",
     "childCount",
     "teamCount",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "DepartmentRef": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "name"
    ],
    "additionalProperties": false
   },
   "DepartmentPerson": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     }
    },
    "required": [
     "id",
     "name",
     "status"
    ],
    "additionalProperties": false
   },
   "DepartmentImportResult": {
    "type": "object",
    "properties": {
     "dryRun": {
      "type": "boolean"
     },
     "created": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Departments created (or that would be) for attribute values no department matched."
     },
     "assigned": {
      "type": "number",
      "description": "People placed in (or moved to) the department their attribute names."
     },
     "unchanged": {
      "type": "number",
      "description": "People already in the matching department."
     },
     "unmatched": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Distinct attribute values that matched no department (only without `createMissing`)."
     },
     "missing": {
      "type": "number",
      "description": "Active people without the attribute."
     }
    },
    "required": [
     "dryRun",
     "created",
     "assigned",
     "unchanged",
     "unmatched",
     "missing"
    ],
    "additionalProperties": false
   },
   "DepartmentSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "headId": {
      "type": "string"
     },
     "costCenter": {
      "type": "string"
     },
     "memberCount": {
      "type": "number",
      "description": "People in this department itself."
     },
     "totalMemberCount": {
      "type": "number",
      "description": "People in it and in every department below it."
     },
     "childCount": {
      "type": "number"
     },
     "teamCount": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "memberCount",
     "totalMemberCount",
     "childCount",
     "teamCount",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "DepartmentMemberView": {
    "type": "object",
    "properties": {
     "department": {
      "$ref": "#/components/schemas/DepartmentRef"
     },
     "title": {
      "type": "string"
     },
     "since": {
      "type": "number"
     },
     "head": {
      "type": "boolean",
      "description": "Set when the person heads their department."
     },
     "managerId": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     }
    },
    "required": [
     "department",
     "since",
     "id",
     "name",
     "status"
    ],
    "additionalProperties": false
   },
   "MyDepartment": {
    "type": "object",
    "properties": {
     "department": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/DepartmentPlacement"
       }
      ]
     },
     "leads": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "department": {
         "$ref": "#/components/schemas/DepartmentRef"
        },
        "path": {
         "type": "array",
         "items": {
          "$ref": "#/components/schemas/DepartmentRef"
         }
        },
        "people": {
         "type": "array",
         "items": {
          "$ref": "#/components/schemas/DepartmentMemberView"
         }
        }
       },
       "required": [
        "department",
        "path",
        "people"
       ],
       "additionalProperties": false
      },
      "description": "Departments the person heads, each with its people and those of every department below it."
     }
    },
    "required": [
     "department",
     "leads"
    ],
    "additionalProperties": false
   },
   "DepartmentPlacement": {
    "type": "object",
    "properties": {
     "department": {
      "$ref": "#/components/schemas/DepartmentRef"
     },
     "path": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/DepartmentRef"
      },
      "description": "From the top department down to the person's own."
     },
     "title": {
      "type": "string"
     },
     "since": {
      "type": "number"
     },
     "head": {
      "$ref": "#/components/schemas/DepartmentPerson"
     },
     "costCenter": {
      "type": "string"
     }
    },
    "required": [
     "department",
     "path",
     "since"
    ],
    "additionalProperties": false
   },
   "BirthrightSuggestion": {
    "type": "object",
    "properties": {
     "unit": {
      "type": "object",
      "properties": {
       "kind": {
        "$ref": "#/components/schemas/OrgUnitKind"
       },
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      },
      "required": [
       "kind",
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "people": {
      "type": "number",
      "description": "Active people a rule naming the unit would match."
     },
     "roles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/BirthrightItem"
      }
     },
     "groups": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/BirthrightItem"
      }
     },
     "wouldGrant": {
      "type": "number",
      "description": "People of the unit who lack at least one suggested item: what creating the package would grant."
     },
     "existingPackages": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Automatic packages whose rule already names the unit or one above it (their contents are left out)."
     },
     "package": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "description": {
        "type": "string"
       },
       "roleIds": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "groupIds": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "autoAssign": {
        "$ref": "#/components/schemas/AutoAssignInput"
       }
      },
      "required": [
       "name",
       "description",
       "roleIds",
       "groupIds",
       "autoAssign"
      ],
      "additionalProperties": false,
      "description": "Ready for `packages.create` (add `tenantId`); the caller becomes the rule's owner."
     }
    },
    "required": [
     "unit",
     "people",
     "roles",
     "groups",
     "wouldGrant",
     "existingPackages",
     "package"
    ],
    "additionalProperties": false
   },
   "OrgUnitKind": {
    "type": "string",
    "enum": [
     "team",
     "department"
    ]
   },
   "BirthrightItem": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "holders": {
      "type": "number",
      "description": "People of the unit who hold it by hand."
     },
     "share": {
      "type": "number",
      "description": "holders / people, rounded to two decimals."
     }
    },
    "required": [
     "id",
     "name",
     "holders",
     "share"
    ],
    "additionalProperties": false
   },
   "AutoAssignInput": {
    "type": "object",
    "properties": {
     "include": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      }
     },
     "exclude": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      }
     },
     "graceMs": {
      "type": "number"
     },
     "maxGrants": {
      "type": "number"
     },
     "maxRemovals": {
      "type": "number"
     }
    },
    "required": [
     "include"
    ],
    "additionalProperties": false
   },
   "ManagerSyncResult": {
    "type": "object",
    "properties": {
     "dryRun": {
      "type": "boolean"
     },
     "updated": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "identityId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "managerId": {
         "type": "string"
        },
        "managerName": {
         "type": "string"
        }
       },
       "required": [
        "identityId",
        "name",
        "managerId",
        "managerName"
       ],
       "additionalProperties": false
      },
      "description": "People whose manager is (or would be) set to their department's head, with both names for display."
     },
     "kept": {
      "type": "number",
      "description": "People left as they were because they already have another manager (without `overwrite`)."
     },
     "noHead": {
      "type": "number",
      "description": "People whose department (and the ones above it) has no head, or who head it with nobody above."
     }
    },
    "required": [
     "dryRun",
     "updated",
     "kept",
     "noHead"
    ],
    "additionalProperties": false
   },
   "DepartmentNode": {
    "type": "object",
    "properties": {
     "head": {
      "$ref": "#/components/schemas/DepartmentPerson"
     },
     "children": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/DepartmentNode"
      }
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "headId": {
      "type": "string"
     },
     "costCenter": {
      "type": "string"
     },
     "memberCount": {
      "type": "number",
      "description": "People in this department itself."
     },
     "totalMemberCount": {
      "type": "number",
      "description": "People in it and in every department below it."
     },
     "childCount": {
      "type": "number"
     },
     "teamCount": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "children",
     "id",
     "tenantId",
     "name",
     "memberCount",
     "totalMemberCount",
     "childCount",
     "teamCount",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "DepartmentUpdate": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "departmentId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "code": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "null (or an empty string) clears the optional fields."
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "parentId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "headId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "costCenter": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     }
    },
    "required": [
     "tenantId",
     "departmentId"
    ],
    "additionalProperties": false
   },
   "DomainDiscovery": {
    "type": "object",
    "properties": {
     "domain": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "type": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "allowedMethods": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/AuthMethod"
        }
       }
      ],
      "description": "Accepted sign-in methods; null means every method the deployment enables."
     },
     "requireMfa": {
      "type": "boolean"
     },
     "region": {
      "type": "string",
      "description": "The organization's home region, in a multi-region deployment."
     },
     "signInUrl": {
      "type": "string",
      "description": "The organization's canonical sign-in URL, when organization addresses or regions are configured."
     }
    },
    "required": [
     "domain",
     "tenantId",
     "name",
     "type",
     "allowedMethods",
     "requireMfa"
    ],
    "additionalProperties": false
   },
   "AuthMethod": {
    "type": "string",
    "enum": [
     "password",
     "passwordless-email",
     "passwordless-sms",
     "passkey",
     "federated",
     "impersonation"
    ]
   },
   "FeatureFlagDefinition": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "key": {
      "type": "string"
     },
     "tenantId": {
      "type": "string",
      "description": "The defining tenant."
     },
     "scope": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ],
      "description": "`platform` when the root tenant defines the flag."
     },
     "description": {
      "type": "string"
     },
     "defaultValue": {
      "type": "boolean"
     },
     "rolloutPercentage": {
      "type": "number"
     },
     "tenantOverridable": {
      "type": "boolean"
     },
     "killSwitch": {
      "type": "boolean"
     },
     "internal": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "updatedAt": {
      "type": "number"
     },
     "updatedBy": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "key",
     "tenantId",
     "scope",
     "defaultValue",
     "tenantOverridable",
     "killSwitch",
     "internal",
     "createdAt",
     "createdBy",
     "updatedAt",
     "updatedBy"
    ],
    "additionalProperties": false
   },
   "FeatureFlagView": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "scope": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ]
     },
     "definedBy": {
      "type": "string",
      "description": "The defining tenant: this tenant or one of its ancestors."
     },
     "definition": {
      "$ref": "#/components/schemas/FeatureFlagDefinition"
     },
     "tenantOverridable": {
      "type": "boolean"
     },
     "evaluation": {
      "$ref": "#/components/schemas/FeatureEvaluation"
     },
     "override": {
      "type": "object",
      "properties": {
       "value": {
        "type": "boolean"
       },
       "setAt": {
        "type": "number"
       },
       "setBy": {
        "type": "string"
       }
      },
      "required": [
       "value",
       "setAt",
       "setBy"
      ],
      "additionalProperties": false,
      "description": "The value this tenant chose (`setOverride`); it applies only while the evaluation's reason says `OVERRIDE`."
     },
     "target": {
      "type": "object",
      "properties": {
       "value": {
        "type": "boolean"
       },
       "locked": {
        "type": "boolean"
       },
       "expiresAt": {
        "type": "number"
       }
      },
      "required": [
       "value",
       "locked"
      ],
      "additionalProperties": false,
      "description": "The value the flag's managers pinned for this tenant (`setTarget`)."
     }
    },
    "required": [
     "key",
     "scope",
     "definedBy",
     "tenantOverridable",
     "evaluation"
    ],
    "additionalProperties": false
   },
   "FeatureEvaluation": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "value": {
      "type": "boolean"
     },
     "reason": {
      "$ref": "#/components/schemas/FeatureReason"
     },
     "scope": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ],
      "description": "`platform` for flags the root tenant defines, `tenant` for flags of an organization or project."
     },
     "definedBy": {
      "type": "string",
      "description": "The defining tenant (absent for `UNKNOWN`)."
     },
     "decidedBy": {
      "type": "string",
      "description": "The tenant whose target or override decided: the evaluated tenant or one of its ancestors."
     },
     "expiresAt": {
      "type": "number",
      "description": "When the deciding target or override lapses."
     },
     "locked": {
      "type": "boolean",
      "description": "A locked target at or above the tenant keeps overrides from applying."
     },
     "overridable": {
      "type": "boolean",
      "description": "Whether the evaluated tenant's administrators may set their own value now (`features.setOverride`)."
     }
    },
    "required": [
     "key",
     "value",
     "reason",
     "scope",
     "locked",
     "overridable"
    ],
    "additionalProperties": false
   },
   "FeatureReason": {
    "type": "string",
    "enum": [
     "KILL_SWITCH",
     "TARGET",
     "OVERRIDE",
     "ROLLOUT",
     "DEFAULT",
     "UNKNOWN"
    ]
   },
   "FeatureTargetView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string",
      "description": "The tenant the value applies to, with its descendants."
     },
     "tenantName": {
      "type": "string"
     },
     "tenantStatus": {
      "$ref": "#/components/schemas/TenantStatus"
     },
     "source": {
      "type": "string",
      "enum": [
       "target",
       "override"
      ]
     },
     "value": {
      "type": "boolean"
     },
     "locked": {
      "type": "boolean"
     },
     "expiresAt": {
      "type": "number"
     },
     "active": {
      "type": "boolean",
      "description": "Past `expiresAt`, or an override the flag no longer allows: kept, but ignored by evaluation."
     },
     "note": {
      "type": "string"
     },
     "setAt": {
      "type": "number"
     },
     "setBy": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "tenantId",
     "tenantName",
     "tenantStatus",
     "source",
     "value",
     "locked",
     "active",
     "setAt",
     "setBy"
    ],
    "additionalProperties": false
   },
   "TenantStatus": {
    "type": "string",
    "enum": [
     "pending",
     "active",
     "deleted",
     "suspended"
    ]
   },
   "GroupMember": {
    "type": "object",
    "properties": {
     "groupId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number",
      "description": "Temporary membership: past this time the person is no longer a member; the purge worker removes the record."
     },
     "packageAssignmentId": {
      "type": "string",
      "description": "Set when an access-package assignment created this membership; revoking the assignment removes it."
     },
     "teamId": {
      "type": "string",
      "description": "Set on memberships of a team's backing group: the team that owns the group (teams.ts writes these)."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "groupId",
     "identityId",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "GroupInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "Group": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "teamId": {
      "type": "string",
      "description": "Set on a team's backing group (teams.ts): its memberships come from the team and are refused elsewhere (TEAM_MANAGED)."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "PublicHostname": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "hostname": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "pending",
       "verified"
      ]
     },
     "primary": {
      "type": "boolean"
     },
     "url": {
      "type": "string",
      "description": "The sign-in URL at this hostname."
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "verifiedAt": {
      "type": "number"
     },
     "lastCheckedAt": {
      "type": "number"
     },
     "dnsRecords": {
      "type": "object",
      "properties": {
       "verification": {
        "type": "object",
        "properties": {
         "type": {
          "type": "string",
          "const": "TXT"
         },
         "name": {
          "type": "string"
         },
         "value": {
          "type": "string"
         }
        },
        "required": [
         "type",
         "name",
         "value"
        ],
        "additionalProperties": false,
        "description": "Proves the organization controls the hostname; publish it, then call `verify`."
       },
       "routing": {
        "type": "object",
        "properties": {
         "type": {
          "type": "string",
          "const": "CNAME"
         },
         "name": {
          "type": "string"
         },
         "value": {
          "type": "string"
         }
        },
        "required": [
         "type",
         "name",
         "value"
        ],
        "additionalProperties": false,
        "description": "Sends the hostname's traffic to the deployment (when `hosts.cnameTarget` is configured)."
       }
      },
      "required": [
       "verification"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "id",
     "tenantId",
     "hostname",
     "status",
     "primary",
     "url",
     "createdAt",
     "createdBy",
     "dnsRecords"
    ],
    "additionalProperties": false
   },
   "EnrollmentResult": {
    "anyOf": [
     {
      "allOf": [
       {
        "type": "object",
        "properties": {
         "identity": {
          "$ref": "#/components/schemas/PublicIdentity"
         }
        },
        "required": [
         "identity"
        ],
        "additionalProperties": false
       },
       {
        "$ref": "#/components/schemas/SessionResult"
       }
      ]
     },
     {
      "allOf": [
       {
        "type": "object",
        "properties": {
         "identity": {
          "$ref": "#/components/schemas/PublicIdentity"
         }
        },
        "required": [
         "identity"
        ],
        "additionalProperties": false
       },
       {
        "$ref": "#/components/schemas/MfaRequired"
       }
      ]
     }
    ]
   },
   "Relationship": {
    "type": "object",
    "properties": {
     "type": {
      "type": "string"
     },
     "resourceId": {
      "type": "string"
     },
     "relation": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "type",
     "resourceId",
     "relation",
     "subjectType",
     "subjectId",
     "createdAt",
     "createdBy",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "ImpactChange": {
    "type": "object",
    "properties": {
     "role": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "description": {
        "type": "string"
       },
       "roleId": {
        "type": "string"
       },
       "policyIds": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "permissions": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "document": {
        "anyOf": [
         {
          "type": "null"
         },
         {
          "$ref": "#/components/schemas/PolicyDocument"
         }
        ]
       },
       "inherits": {
        "type": "array",
        "items": {
         "type": "string"
        },
        "description": "Replaces the inherited roles; an empty list clears inheritance."
       }
      },
      "required": [
       "roleId"
      ],
      "additionalProperties": false
     },
     "policy": {
      "type": "object",
      "properties": {
       "policyId": {
        "type": "string"
       },
       "document": {
        "$ref": "#/components/schemas/PolicyDocument"
       }
      },
      "required": [
       "policyId",
       "document"
      ],
      "additionalProperties": false
     },
     "deleteRole": {
      "type": "string"
     }
    },
    "additionalProperties": false
   },
   "ImpactPreview": {
    "type": "object",
    "properties": {
     "roles": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "name": {
         "type": "string"
        }
       },
       "required": [
        "id",
        "name"
       ],
       "additionalProperties": false
      },
      "description": "Roles whose grants the change touches: the changed roles and every role inheriting them."
     },
     "evaluated": {
      "type": "number",
      "description": "People and service accounts holding those roles (directly or through groups) that were evaluated."
     },
     "truncated": {
      "type": "boolean",
      "description": "Holders beyond the evaluation cap (200) were skipped."
     },
     "identities": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ImpactIdentity"
      },
      "description": "Holders whose allowed actions change on at least one resource."
     },
     "gainedTotal": {
      "type": "number"
     },
     "lostTotal": {
      "type": "number"
     },
     "invariants": {
      "type": "object",
      "properties": {
       "broken": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          },
          "mode": {
           "type": "string",
           "enum": [
            "enforce",
            "monitor"
           ]
          },
          "violations": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/InvariantViolation"
           }
          }
         },
         "required": [
          "id",
          "name",
          "mode",
          "violations"
         ],
         "additionalProperties": false
        }
       },
       "fixed": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string"
          },
          "name": {
           "type": "string"
          },
          "mode": {
           "type": "string",
           "enum": [
            "enforce",
            "monitor"
           ]
          }
         },
         "required": [
          "id",
          "name",
          "mode"
         ],
         "additionalProperties": false
        }
       }
      },
      "required": [
       "broken",
       "fixed"
      ],
      "additionalProperties": false,
      "description": "Access invariants the change would newly break (with the new violations) or make pass again."
     }
    },
    "required": [
     "roles",
     "evaluated",
     "truncated",
     "identities",
     "gainedTotal",
     "lostTotal",
     "invariants"
    ],
    "additionalProperties": false
   },
   "ImpactIdentity": {
    "type": "object",
    "properties": {
     "identity": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "changes": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ImpactResourceDiff"
      }
     }
    },
    "required": [
     "identity",
     "changes"
    ],
    "additionalProperties": false
   },
   "ImpactResourceDiff": {
    "type": "object",
    "properties": {
     "resource": {
      "type": "string",
      "description": "`type/id` of the resource evaluated."
     },
     "gained": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "lost": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "resource",
     "gained",
     "lost"
    ],
    "additionalProperties": false
   },
   "InvariantViolation": {
    "type": "object",
    "properties": {
     "identity": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "reason": {
      "type": "string",
      "description": "The decision reason: why the action was allowed, or why it was denied."
     }
    },
    "required": [
     "identity",
     "reason"
    ],
    "additionalProperties": false
   },
   "PublicModel": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "inherited": {
      "type": "boolean"
     },
     "provider": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "kind": {
        "$ref": "#/components/schemas/ProviderKind"
       }
      },
      "required": [
       "id",
       "name",
       "kind"
      ],
      "additionalProperties": false
     },
     "upstreamModel": {
      "type": "string"
     },
     "displayName": {
      "type": "string"
     },
     "family": {
      "type": "string"
     },
     "tier": {
      "type": "string"
     },
     "contextWindow": {
      "type": "number"
     },
     "maxOutputTokens": {
      "type": "number"
     },
     "inputPricePerMTok": {
      "type": "number"
     },
     "outputPricePerMTok": {
      "type": "number"
     },
     "cachedInputPricePerMTok": {
      "type": "number"
     },
     "fallbacks": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "providerTools": {
      "type": "string",
      "enum": [
       "allow",
       "deny",
       "policy"
      ],
      "description": "Provider-run tools: `deny` or `policy` when set (see `InferenceModel.providerTools`)."
     },
     "enabled": {
      "type": "boolean"
     }
    },
    "required": [
     "name",
     "tenantId",
     "inherited",
     "provider",
     "upstreamModel",
     "enabled"
    ],
    "additionalProperties": false
   },
   "ProviderKind": {
    "type": "string",
    "enum": [
     "anthropic",
     "openai",
     "openai-compatible"
    ]
   },
   "BudgetStanding": {
    "type": "object",
    "properties": {
     "budgetId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriod"
     },
     "windowStart": {
      "type": "number"
     },
     "resetsAt": {
      "type": "number"
     },
     "usedTokens": {
      "type": "number"
     },
     "usedCostMicros": {
      "type": "number"
     },
     "usedRequests": {
      "type": "number",
      "description": "Calls metered in the window."
     },
     "remainingTokens": {
      "type": "number"
     },
     "remainingCostMicros": {
      "type": "number"
     },
     "remainingRequests": {
      "type": "number"
     }
    },
    "required": [
     "budgetId",
     "name",
     "period",
     "windowStart",
     "resetsAt",
     "usedTokens",
     "usedCostMicros",
     "usedRequests"
    ],
    "additionalProperties": false
   },
   "ModelInput": {
    "type": "object",
    "properties": {
     "providerId": {
      "type": "string"
     },
     "upstreamModel": {
      "type": "string"
     },
     "displayName": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "family": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "tier": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "contextWindow": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "maxOutputTokens": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "inputPricePerMTok": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "outputPricePerMTok": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "cachedInputPricePerMTok": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "fallbacks": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ],
      "description": "Models to try, in order, when this one's provider fails (at most 5; null clears)."
     },
     "providerTools": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string",
        "const": "allow"
       },
       {
        "type": "string",
        "const": "deny"
       },
       {
        "type": "string",
        "const": "policy"
       }
      ],
      "description": "Provider-run tools: `allow` (the default; null too), `deny`, or `policy` (`inference:use-tool` per tool)."
     },
     "enabled": {
      "type": "boolean"
     }
    },
    "additionalProperties": false
   },
   "PublicProvider": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "kind": {
      "$ref": "#/components/schemas/ProviderKind"
     },
     "baseUrl": {
      "type": "string"
     },
     "keyHint": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "keyRotatedAt": {
      "type": "number"
     },
     "inherited": {
      "type": "boolean",
      "description": "Defined by an ancestor tenant."
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "kind",
     "baseUrl",
     "keyHint",
     "createdAt",
     "updatedAt",
     "keyRotatedAt",
     "inherited"
    ],
    "additionalProperties": false
   },
   "InferenceBudgetView": {
    "type": "object",
    "properties": {
     "maxCostUsd": {
      "type": "number"
     },
     "subjectName": {
      "type": "string"
     },
     "standing": {
      "$ref": "#/components/schemas/BudgetStanding"
     },
     "name": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "tenant",
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string",
      "description": "The tenant, group or identity the budget covers."
     },
     "scope": {
      "type": "string",
      "enum": [
       "shared",
       "each"
      ],
      "description": "`shared`: one pool for everyone covered; `each`: every identity covered gets the full amount."
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriod"
     },
     "maxTokens": {
      "type": "number"
     },
     "maxCostMicros": {
      "type": "number"
     },
     "maxRequests": {
      "type": "number",
      "description": "The most calls per window (a rate limit for agents that loop)."
     },
     "models": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Model name patterns the budget counts (every model when absent)."
     },
     "alertAtPercent": {
      "type": "number",
      "description": "Emits `inference:budget-alert` once per window when usage crosses this share of a limit."
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "subjectType",
     "subjectId",
     "scope",
     "period",
     "createdAt",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "UsageReport": {
    "type": "object",
    "properties": {
     "from": {
      "type": "number"
     },
     "to": {
      "type": "number"
     },
     "groupBy": {
      "type": "string",
      "enum": [
       "agent",
       "identity",
       "day",
       "model"
      ]
     },
     "rows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/UsageRow"
      }
     },
     "totals": {
      "$ref": "#/components/schemas/UsageRow"
     }
    },
    "required": [
     "from",
     "to",
     "groupBy",
     "rows",
     "totals"
    ],
    "additionalProperties": false
   },
   "UsageRow": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "label": {
      "type": "string"
     },
     "requests": {
      "type": "number"
     },
     "errors": {
      "type": "number"
     },
     "inputTokens": {
      "type": "number"
     },
     "outputTokens": {
      "type": "number"
     },
     "cacheReadTokens": {
      "type": "number"
     },
     "cacheWriteTokens": {
      "type": "number"
     },
     "costMicros": {
      "type": "number"
     },
     "costUsd": {
      "type": "number"
     }
    },
    "required": [
     "key",
     "requests",
     "errors",
     "inputTokens",
     "outputTokens",
     "cacheReadTokens",
     "cacheWriteTokens",
     "costMicros",
     "costUsd"
    ],
    "additionalProperties": false
   },
   "InferenceTokenUsage": {
    "type": "object",
    "properties": {
     "inputTokens": {
      "type": "number"
     },
     "outputTokens": {
      "type": "number"
     },
     "cacheReadTokens": {
      "type": "number"
     },
     "cacheWriteTokens": {
      "type": "number"
     }
    },
    "required": [
     "inputTokens",
     "outputTokens"
    ],
    "additionalProperties": false
   },
   "InferenceBudgetInput": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "tenant",
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string",
      "description": "The group or identity; the tenant itself for `tenant` budgets (may be left out)."
     },
     "scope": {
      "type": "string",
      "enum": [
       "shared",
       "each"
      ]
     },
     "period": {
      "$ref": "#/components/schemas/BudgetPeriod"
     },
     "maxTokens": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "maxCostUsd": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ],
      "description": "A cost cap in US dollars (stored in micro-dollars)."
     },
     "maxRequests": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ],
      "description": "The most calls per window."
     },
     "models": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "alertAtPercent": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     }
    },
    "required": [
     "name",
     "subjectType",
     "period"
    ],
    "additionalProperties": false
   },
   "InvariantInput": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "subject": {
      "$ref": "#/components/schemas/InvariantSubject"
     },
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "expect": {
      "type": "string",
      "enum": [
       "allow",
       "deny"
      ]
     },
     "mode": {
      "type": "string",
      "enum": [
       "enforce",
       "monitor"
      ]
     },
     "assumeMfa": {
      "type": "boolean"
     }
    },
    "required": [
     "name",
     "subject",
     "action",
     "resource",
     "expect"
    ],
    "additionalProperties": false
   },
   "InvariantSubject": {
    "anyOf": [
     {
      "type": "object",
      "properties": {
       "identityId": {
        "type": "string"
       }
      },
      "required": [
       "identityId"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "groupId": {
        "type": "string"
       }
      },
      "required": [
       "groupId"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "attribute": {
        "type": "object",
        "properties": {
         "name": {
          "type": "string"
         },
         "value": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           }
          ]
         }
        },
        "required": [
         "name",
         "value"
        ],
        "additionalProperties": false
       }
      },
      "required": [
       "attribute"
      ],
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "everyone": {
        "type": "boolean",
        "const": true
       }
      },
      "required": [
       "everyone"
      ],
      "additionalProperties": false
     }
    ]
   },
   "AccessInvariant": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "subject": {
      "$ref": "#/components/schemas/InvariantSubject"
     },
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "expect": {
      "type": "string",
      "enum": [
       "allow",
       "deny"
      ]
     },
     "mode": {
      "type": "string",
      "enum": [
       "enforce",
       "monitor"
      ]
     },
     "assumeMfa": {
      "type": "boolean",
      "description": "Evaluate subjects as MFA-verified sessions (default true: the most a person can reach)."
     },
     "createdBy": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "lastCheck": {
      "type": "object",
      "properties": {
       "at": {
        "type": "number"
       },
       "passed": {
        "type": "boolean"
       },
       "violations": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "error": {
        "type": "string"
       }
      },
      "required": [
       "at",
       "passed",
       "violations"
      ],
      "additionalProperties": false,
      "description": "The last scheduled check (`iam.checkInvariants`): whether it passed and who violated it."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "subject",
     "action",
     "resource",
     "expect",
     "mode",
     "assumeMfa",
     "createdBy",
     "createdAt",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "InvariantResult": {
    "type": "object",
    "properties": {
     "invariant": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "id": {
        "type": "string"
       },
       "mode": {
        "type": "string",
        "enum": [
         "enforce",
         "monitor"
        ]
       },
       "expect": {
        "type": "string",
        "enum": [
         "allow",
         "deny"
        ]
       },
       "action": {
        "type": "string"
       },
       "resource": {
        "type": "object",
        "properties": {
         "type": {
          "type": "string"
         },
         "id": {
          "type": "string"
         }
        },
        "required": [
         "type",
         "id"
        ],
        "additionalProperties": false
       }
      },
      "required": [
       "name",
       "id",
       "mode",
       "expect",
       "action",
       "resource"
      ],
      "additionalProperties": false
     },
     "passed": {
      "type": "boolean"
     },
     "evaluated": {
      "type": "number"
     },
     "truncated": {
      "type": "boolean",
      "description": "Subjects beyond the evaluation cap (500) were not checked."
     },
     "violations": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/InvariantViolation"
      }
     },
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       }
      },
      "required": [
       "code",
       "message"
      ],
      "additionalProperties": false,
      "description": "Set when the invariant could not be evaluated (its resource or group no longer exists, for example)."
     }
    },
    "required": [
     "invariant",
     "passed",
     "evaluated",
     "truncated",
     "violations"
    ],
    "additionalProperties": false
   },
   "InvariantRunResult": {
    "type": "object",
    "properties": {
     "generatedAt": {
      "type": "number"
     },
     "summary": {
      "type": "object",
      "properties": {
       "passed": {
        "type": "number"
       },
       "failed": {
        "type": "number"
       },
       "errors": {
        "type": "number"
       }
      },
      "required": [
       "passed",
       "failed",
       "errors"
      ],
      "additionalProperties": false
     },
     "results": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/InvariantResult"
      }
     }
    },
    "required": [
     "generatedAt",
     "summary",
     "results"
    ],
    "additionalProperties": false
   },
   "IdentityLink": {
    "type": "object",
    "properties": {
     "leftId": {
      "type": "string"
     },
     "rightId": {
      "type": "string"
     },
     "revoked": {
      "type": "boolean"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "leftId",
     "rightId",
     "revoked",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "LinkedAccount": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     },
     "tenantId": {
      "type": "string"
     },
     "tenantName": {
      "type": "string"
     },
     "tenantSlug": {
      "type": "string"
     },
     "tenantStatus": {
      "$ref": "#/components/schemas/TenantStatus"
     }
    },
    "required": [
     "id",
     "identityId",
     "name",
     "status",
     "tenantId",
     "tenantName",
     "tenantStatus"
    ],
    "additionalProperties": false
   },
   "OidcProviderCreateInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string",
      "description": "A label for administrators, at most 128 characters."
     },
     "issuer": {
      "type": "string",
      "description": "The exact `iss` of the provider's tokens: https, no credentials, query or fragment, at most 512 characters."
     },
     "audiences": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Accepted `aud` values (1 to 10 of at most 256 characters); a token must name one of them."
     },
     "jwksUri": {
      "type": "string",
      "description": "Where the keys live (https on port 443). Without it and without `jwks`, OIDC discovery on the issuer."
     },
     "jwks": {
      "type": "object",
      "properties": {
       "keys": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/PublicJwk"
        }
       }
      },
      "required": [
       "keys"
      ],
      "additionalProperties": false,
      "description": "Static public keys (1 to 20), used instead of fetching."
     },
     "algorithms": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/WebIdentityAlgorithm"
      },
      "description": "Accepted signature algorithms (default ['RS256', 'ES256'])."
     },
     "maxTokenLifetimeSeconds": {
      "type": "number",
      "description": "Longest accepted token lifetime and age, 60..86400 seconds (default 3600)."
     },
     "clockToleranceSeconds": {
      "type": "number",
      "description": "Clock skew tolerated on the time claims, 0..120 seconds (default 30)."
     },
     "replayProtection": {
      "type": "string",
      "enum": [
       "single-use",
       "off"
      ],
      "description": "'single-use' (default) redeems each token once; 'off' suits tokens SDKs reuse until they rotate."
     },
     "enabled": {
      "type": "boolean",
      "description": "Default true."
     }
    },
    "required": [
     "tenantId",
     "name",
     "issuer",
     "audiences"
    ],
    "additionalProperties": false
   },
   "PublicJwk": {
    "type": "object",
    "properties": {
     "kty": {
      "type": "string"
     },
     "kid": {
      "type": "string"
     },
     "alg": {
      "type": "string"
     },
     "use": {
      "type": "string"
     }
    },
    "required": [
     "kty"
    ],
    "additionalProperties": {}
   },
   "WebIdentityAlgorithm": {
    "type": "string",
    "enum": [
     "RS256",
     "RS384",
     "RS512",
     "PS256",
     "PS384",
     "PS512",
     "ES256",
     "ES384",
     "EdDSA"
    ]
   },
   "PublicOidcProvider": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "issuer": {
      "type": "string"
     },
     "audiences": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "jwksUri": {
      "type": "string"
     },
     "jwks": {
      "type": "object",
      "properties": {
       "keys": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/PublicJwk"
        }
       }
      },
      "required": [
       "keys"
      ],
      "additionalProperties": false
     },
     "algorithms": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/WebIdentityAlgorithm"
      }
     },
     "maxTokenLifetimeSeconds": {
      "type": "number"
     },
     "clockToleranceSeconds": {
      "type": "number"
     },
     "replayProtection": {
      "type": "string",
      "enum": [
       "single-use",
       "off"
      ]
     },
     "enabled": {
      "type": "boolean"
     },
     "authorityId": {
      "type": "string"
     },
     "sessionsRevokedBefore": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "issuer",
     "audiences",
     "algorithms",
     "maxTokenLifetimeSeconds",
     "clockToleranceSeconds",
     "replayProtection",
     "enabled",
     "authorityId",
     "createdAt",
     "createdBy",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "OidcProviderRevokeSessionsInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "providerId": {
      "type": "string"
     },
     "before": {
      "type": "number",
      "description": "Epoch milliseconds, no later than now + 1."
     }
    },
    "required": [
     "tenantId",
     "providerId"
    ],
    "additionalProperties": false
   },
   "OidcProviderUpdateInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "providerId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "audiences": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "jwksUri": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "jwks": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "object",
        "properties": {
         "keys": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/PublicJwk"
          }
         }
        },
        "required": [
         "keys"
        ],
        "additionalProperties": false
       }
      ]
     },
     "algorithms": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/WebIdentityAlgorithm"
      }
     },
     "maxTokenLifetimeSeconds": {
      "type": "number"
     },
     "clockToleranceSeconds": {
      "type": "number"
     },
     "replayProtection": {
      "type": "string",
      "enum": [
       "single-use",
       "off"
      ]
     },
     "enabled": {
      "type": "boolean"
     }
    },
    "required": [
     "tenantId",
     "providerId"
    ],
    "additionalProperties": false
   },
   "OnboardingFlowInput": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "audience": {
      "$ref": "#/components/schemas/OnboardingAudience"
     },
     "appliesTo": {
      "type": "string",
      "enum": [
       "tenant",
       "descendants",
       "subtree"
      ],
      "description": "`tenant` (the defining tenant's own members), `descendants` (tenants below it) or `subtree` (both). Defaults to\n`descendants` for tenant flows and flows defined at the platform root, `tenant` elsewhere."
     },
     "tenantTypes": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "rule": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/OnboardingRule"
       }
      ]
     },
     "required": {
      "type": "boolean"
     },
     "locked": {
      "type": "boolean"
     },
     "includeExisting": {
      "type": "boolean"
     },
     "enabled": {
      "type": "boolean"
     },
     "steps": {
      "type": "array",
      "items": {}
     },
     "completionGroupIds": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     }
    },
    "required": [
     "name",
     "audience",
     "steps"
    ],
    "additionalProperties": false
   },
   "OnboardingAudience": {
    "type": "string",
    "enum": [
     "tenant",
     "member"
    ]
   },
   "OnboardingRule": {
    "type": "object",
    "properties": {
     "include": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      }
     },
     "exclude": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      }
     }
    },
    "required": [
     "include"
    ],
    "additionalProperties": false
   },
   "OnboardingFlow": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "audience": {
      "$ref": "#/components/schemas/OnboardingAudience"
     },
     "appliesTo": {
      "$ref": "#/components/schemas/OnboardingScope"
     },
     "tenantTypes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Descendant tenants of these types only (all types when absent)."
     },
     "rule": {
      "$ref": "#/components/schemas/OnboardingRule"
     },
     "required": {
      "type": "boolean",
      "description": "Required flows count toward `principal.pendingOnboarding` until complete."
     },
     "locked": {
      "type": "boolean",
      "description": "Inherited flows a descendant tenant may not switch off. Tenant flows are always binding."
     },
     "includeExisting": {
      "type": "boolean",
      "description": "Also applies to people and tenants that existed before the flow took effect."
     },
     "enabled": {
      "type": "boolean"
     },
     "effectiveFrom": {
      "type": "number",
      "description": "When the flow first took effect; without `includeExisting` only newcomers from then on are asked."
     },
     "steps": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingStep"
      }
     },
     "completionGroupIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Member flows of the defining tenant: groups a person joins when they complete the flow."
     },
     "version": {
      "type": "number",
      "description": "Bumped whenever the steps change."
     },
     "authorId": {
      "type": "string",
      "description": "Who last saved the flow (left out where descendants see an inherited flow)."
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "audience",
     "appliesTo",
     "required",
     "locked",
     "includeExisting",
     "enabled",
     "steps",
     "version",
     "createdAt",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "OnboardingScope": {
    "type": "string",
    "enum": [
     "tenant",
     "descendants",
     "subtree"
    ]
   },
   "OnboardingStep": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string",
      "description": "Stable within the flow: lowercase letters, digits and dashes (at most 40). Progress is recorded per step ID."
     },
     "kind": {
      "$ref": "#/components/schemas/OnboardingStepKind"
     },
     "title": {
      "type": "string"
     },
     "description": {
      "type": "string",
      "description": "Plain text or Markdown shown with the step."
     },
     "optional": {
      "type": "boolean",
      "description": "Optional steps never hold a flow back."
     },
     "fields": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingField"
      }
     },
     "content": {
      "type": "string",
      "description": "`acknowledge`: the text the reader confirms."
     },
     "url": {
      "type": "string",
      "description": "`task`: where the work happens."
     },
     "verification": {
      "type": "string",
      "enum": [
       "self",
       "admin"
      ],
      "description": "`task`: `self` (the default) completes when marked done; `admin` waits for an administrator to verify it."
     },
     "agreement": {
      "type": "string",
      "description": "`agreement`: the name of a terms-of-use agreement in the member's own tenant."
     },
     "check": {
      "type": "string",
      "enum": [
       "agreement",
       "verified-domain",
       "members",
       "owners",
       "mfa-policy",
       "slug",
       "sso",
       "directory-sync",
       "member-onboarding"
      ]
     },
     "minimum": {
      "type": "number",
      "description": "`check` members/owners: how many are needed (default 2)."
     }
    },
    "required": [
     "id",
     "kind",
     "title"
    ],
    "additionalProperties": false
   },
   "OnboardingStepKind": {
    "type": "string",
    "enum": [
     "mfa",
     "passkey",
     "agreement",
     "form",
     "acknowledge",
     "task",
     "verify-email",
     "check"
    ]
   },
   "OnboardingField": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string",
      "description": "Answer key: a letter, then letters, digits or underscores (at most 40)."
     },
     "label": {
      "type": "string"
     },
     "type": {
      "$ref": "#/components/schemas/OnboardingFieldType"
     },
     "required": {
      "type": "boolean",
      "description": "A required boolean must be checked (a confirmation)."
     },
     "help": {
      "type": "string"
     },
     "placeholder": {
      "type": "string"
     },
     "options": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "`select` choices (1-50)."
     },
     "maxLength": {
      "type": "number",
      "description": "Text length cap (text/textarea)."
     },
     "min": {
      "type": "number",
      "description": "Number bounds."
     },
     "max": {
      "type": "number"
     },
     "attribute": {
      "type": "string",
      "description": "Member flows: the declared identity attribute (`permissions.identityAttributes`) this answer fills. Onboarding only\nfills attributes that are empty, so it never overwrites a value an administrator or directory sync set."
     }
    },
    "required": [
     "name",
     "label",
     "type"
    ],
    "additionalProperties": false
   },
   "OnboardingFieldType": {
    "type": "string",
    "enum": [
     "number",
     "boolean",
     "email",
     "url",
     "text",
     "textarea",
     "select",
     "date"
    ]
   },
   "EffectiveOnboarding": {
    "type": "object",
    "properties": {
     "tenant": {
      "$ref": "#/components/schemas/OnboardingSource"
     },
     "levels": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingSource"
      },
      "description": "The tenant's ancestry, root first: the levels flows can come from."
     },
     "memberFlows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/EffectiveOnboardingFlow"
      },
      "description": "Member flows that reach this tenant's people (own and inherited)."
     },
     "setupFlows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/EffectiveOnboardingFlow"
      },
      "description": "Setup flows this tenant's administrators are asked to complete (defined above it)."
     },
     "ownFlows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingFlow"
      },
      "description": "Every flow this tenant defines (for its own people, the tenants below it, or both), oldest first."
     },
     "descendantTypes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Tenant types that can exist below this tenant (what `tenantTypes` may name); empty for a leaf type."
     },
     "identityAttributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "$ref": "#/components/schemas/AttributeType"
      },
      "description": "The declared identity attributes form answers may fill, with their types."
     },
     "settings": {
      "type": "object",
      "properties": {
       "own": {
        "$ref": "#/components/schemas/OnboardingSettings"
       },
       "resolved": {
        "$ref": "#/components/schemas/ResolvedOnboardingSettings"
       }
      },
      "required": [
       "resolved"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "tenant",
     "levels",
     "memberFlows",
     "setupFlows",
     "ownFlows",
     "descendantTypes",
     "identityAttributes",
     "settings"
    ],
    "additionalProperties": false
   },
   "OnboardingSource": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "type": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "name",
     "type"
    ],
    "additionalProperties": false
   },
   "EffectiveOnboardingFlow": {
    "type": "object",
    "properties": {
     "flow": {
      "$ref": "#/components/schemas/OnboardingFlow"
     },
     "source": {
      "$ref": "#/components/schemas/OnboardingSource"
     },
     "inherited": {
      "type": "boolean"
     },
     "disabledBy": {
      "$ref": "#/components/schemas/OnboardingSource"
     },
     "canDisable": {
      "type": "boolean",
      "description": "Whether this tenant may switch the flow off (inherited, unlocked member flows)."
     }
    },
    "required": [
     "flow",
     "source",
     "inherited",
     "canDisable"
    ],
    "additionalProperties": false
   },
   "OnboardingSettings": {
    "type": "object",
    "properties": {
     "welcomeTitle": {
      "type": "string"
     },
     "welcomeMessage": {
      "type": "string"
     },
     "supportEmail": {
      "type": "string"
     },
     "supportUrl": {
      "type": "string"
     },
     "disabledFlowIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Inherited, unlocked member flows switched off for this tenant and every tenant below it."
     },
     "updatedAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "disabledFlowIds",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "ResolvedOnboardingSettings": {
    "type": "object",
    "properties": {
     "welcomeTitle": {
      "type": "string"
     },
     "welcomeMessage": {
      "type": "string"
     },
     "supportEmail": {
      "type": "string"
     },
     "supportUrl": {
      "type": "string"
     },
     "sources": {
      "type": "object",
      "properties": {
       "welcomeTitle": {
        "$ref": "#/components/schemas/OnboardingSource"
       },
       "welcomeMessage": {
        "$ref": "#/components/schemas/OnboardingSource"
       },
       "supportEmail": {
        "$ref": "#/components/schemas/OnboardingSource"
       },
       "supportUrl": {
        "$ref": "#/components/schemas/OnboardingSource"
       }
      },
      "additionalProperties": false,
      "description": "The tenant each value came from (the nearest one that set it)."
     }
    },
    "required": [
     "sources"
    ],
    "additionalProperties": false
   },
   "OnboardingFlowStatus": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "audience": {
      "$ref": "#/components/schemas/OnboardingAudience"
     },
     "required": {
      "type": "boolean"
     },
     "locked": {
      "type": "boolean"
     },
     "version": {
      "type": "number"
     },
     "source": {
      "$ref": "#/components/schemas/OnboardingSource"
     },
     "inherited": {
      "type": "boolean"
     },
     "steps": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingStepStatus"
      }
     },
     "complete": {
      "type": "boolean"
     },
     "completedAt": {
      "type": "number"
     },
     "done": {
      "type": "number"
     },
     "total": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "name",
     "audience",
     "required",
     "locked",
     "version",
     "source",
     "inherited",
     "steps",
     "complete",
     "done",
     "total"
    ],
    "additionalProperties": false
   },
   "OnboardingStepStatus": {
    "type": "object",
    "properties": {
     "optional": {
      "type": "boolean",
      "description": "Optional steps never hold a flow back."
     },
     "state": {
      "$ref": "#/components/schemas/OnboardingStepState"
     },
     "completedAt": {
      "type": "number"
     },
     "submittedAt": {
      "type": "number"
     },
     "rejectedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     },
     "answers": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "detail": {
      "type": "string",
      "description": "A human-readable fact behind a self-completing step, such as `1 of 2 members`."
     },
     "id": {
      "type": "string",
      "description": "Stable within the flow: lowercase letters, digits and dashes (at most 40). Progress is recorded per step ID."
     },
     "kind": {
      "$ref": "#/components/schemas/OnboardingStepKind"
     },
     "title": {
      "type": "string"
     },
     "description": {
      "type": "string",
      "description": "Plain text or Markdown shown with the step."
     },
     "fields": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingField"
      }
     },
     "content": {
      "type": "string",
      "description": "`acknowledge`: the text the reader confirms."
     },
     "url": {
      "type": "string",
      "description": "`task`: where the work happens."
     },
     "verification": {
      "type": "string",
      "enum": [
       "self",
       "admin"
      ],
      "description": "`task`: `self` (the default) completes when marked done; `admin` waits for an administrator to verify it."
     },
     "agreement": {
      "type": "string",
      "description": "`agreement`: the name of a terms-of-use agreement in the member's own tenant."
     },
     "check": {
      "type": "string",
      "enum": [
       "agreement",
       "verified-domain",
       "members",
       "owners",
       "mfa-policy",
       "slug",
       "sso",
       "directory-sync",
       "member-onboarding"
      ]
     },
     "minimum": {
      "type": "number",
      "description": "`check` members/owners: how many are needed (default 2)."
     }
    },
    "required": [
     "optional",
     "state",
     "id",
     "kind",
     "title"
    ],
    "additionalProperties": false
   },
   "OnboardingStepState": {
    "type": "string",
    "enum": [
     "pending",
     "rejected",
     "complete",
     "submitted",
     "unavailable"
    ]
   },
   "MyOnboarding": {
    "type": "object",
    "properties": {
     "tenant": {
      "$ref": "#/components/schemas/OnboardingSource"
     },
     "welcome": {
      "$ref": "#/components/schemas/ResolvedOnboardingSettings"
     },
     "flows": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OnboardingFlowStatus"
      }
     },
     "pending": {
      "type": "number",
      "description": "Required flows still open."
     },
     "complete": {
      "type": "boolean"
     }
    },
    "required": [
     "tenant",
     "welcome",
     "flows",
     "pending",
     "complete"
    ],
    "additionalProperties": false
   },
   "OnboardingProgressReport": {
    "type": "object",
    "properties": {
     "flow": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "audience": {
        "$ref": "#/components/schemas/OnboardingAudience"
       },
       "version": {
        "type": "number"
       },
       "required": {
        "type": "boolean"
       },
       "source": {
        "$ref": "#/components/schemas/OnboardingSource"
       },
       "inherited": {
        "type": "boolean"
       },
       "disabledBy": {
        "$ref": "#/components/schemas/OnboardingSource"
       }
      },
      "required": [
       "id",
       "name",
       "audience",
       "version",
       "required",
       "source",
       "inherited"
      ],
      "additionalProperties": false
     },
     "members": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "type": "object",
         "properties": {
          "identity": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "name": {
             "type": "string"
            },
            "email": {
             "type": "string"
            }
           },
           "required": [
            "id",
            "name"
           ],
           "additionalProperties": false
          }
         },
         "required": [
          "identity"
         ],
         "additionalProperties": false
        },
        {
         "$ref": "#/components/schemas/OnboardingSubjectProgress"
        }
       ]
      },
      "description": "Member flows: this tenant's people the flow applies to."
     },
     "descendants": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenant": {
         "$ref": "#/components/schemas/OnboardingSource"
        },
        "people": {
         "type": "number"
        },
        "complete": {
         "type": "number"
        }
       },
       "required": [
        "tenant",
        "people",
        "complete"
       ],
       "additionalProperties": false
      },
      "description": "Member flows defined here that reach descendant tenants: per-tenant counts, never names."
     },
     "tenants": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "type": "object",
         "properties": {
          "tenant": {
           "allOf": [
            {
             "$ref": "#/components/schemas/OnboardingSource"
            },
            {
             "type": "object",
             "properties": {
              "status": {
               "type": "string"
              }
             },
             "required": [
              "status"
             ],
             "additionalProperties": false
            }
           ]
          }
         },
         "required": [
          "tenant"
         ],
         "additionalProperties": false
        },
        {
         "$ref": "#/components/schemas/OnboardingSubjectProgress"
        }
       ]
      },
      "description": "Tenant flows defined here: every descendant tenant the flow applies to."
     },
     "summary": {
      "type": "object",
      "properties": {
       "subjects": {
        "type": "number"
       },
       "complete": {
        "type": "number"
       }
      },
      "required": [
       "subjects",
       "complete"
      ],
      "additionalProperties": false
     },
     "truncated": {
      "type": "boolean",
      "description": "More than 1000 descendant tenants: the report covers the first 1000."
     }
    },
    "required": [
     "flow",
     "summary"
    ],
    "additionalProperties": false
   },
   "OnboardingSubjectProgress": {
    "type": "object",
    "properties": {
     "complete": {
      "type": "boolean"
     },
     "done": {
      "type": "number"
     },
     "total": {
      "type": "number"
     },
     "completedAt": {
      "type": "number"
     },
     "startedAt": {
      "type": "number"
     },
     "awaiting": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Admin-verified task steps waiting for a decision."
     },
     "answers": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "object",
       "properties": {},
       "additionalProperties": {
        "description": "Any JSON value"
       }
      },
      "description": "Form answers by step ID (people of the reporting tenant, and tenants a flow defined here sets up)."
     },
     "completionError": {
      "type": "string"
     }
    },
    "required": [
     "complete",
     "done",
     "total",
     "awaiting"
    ],
    "additionalProperties": false
   },
   "OnboardingSettingsInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "welcomeTitle": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "welcomeMessage": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "supportEmail": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "supportUrl": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "disabledFlowIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "tenantId"
    ],
    "additionalProperties": false
   },
   "OnboardingFlowUpdate": {
    "allOf": [
     {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "description": {
        "anyOf": [
         {
          "type": "null"
         },
         {
          "type": "string"
         }
        ]
       },
       "required": {
        "type": "boolean"
       },
       "appliesTo": {
        "type": "string",
        "enum": [
         "tenant",
         "descendants",
         "subtree"
        ],
        "description": "`tenant` (the defining tenant's own members), `descendants` (tenants below it) or `subtree` (both). Defaults to\n`descendants` for tenant flows and flows defined at the platform root, `tenant` elsewhere."
       },
       "tenantTypes": {
        "anyOf": [
         {
          "type": "null"
         },
         {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        ]
       },
       "rule": {
        "anyOf": [
         {
          "type": "null"
         },
         {
          "$ref": "#/components/schemas/OnboardingRule"
         }
        ]
       },
       "locked": {
        "type": "boolean"
       },
       "includeExisting": {
        "type": "boolean"
       },
       "enabled": {
        "type": "boolean"
       },
       "steps": {
        "type": "array",
        "items": {}
       },
       "completionGroupIds": {
        "anyOf": [
         {
          "type": "null"
         },
         {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        ]
       }
      },
      "additionalProperties": false
     },
     {
      "type": "object",
      "properties": {
       "tenantId": {
        "type": "string"
       },
       "flowId": {
        "type": "string"
       }
      },
      "required": [
       "tenantId",
       "flowId"
      ],
      "additionalProperties": false
     }
    ]
   },
   "PackageRequestSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "packageId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "status": {
      "$ref": "#/components/schemas/PackageRequestStatus"
     },
     "requestedAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "desiredExpiresAt": {
      "type": "number"
     },
     "justification": {
      "type": "string"
     },
     "decidedBy": {
      "type": "string"
     },
     "decidedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     },
     "assignmentId": {
      "type": "string"
     },
     "packageName": {
      "type": "string"
     },
     "identityName": {
      "type": "string"
     },
     "identityEmail": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "tenantId",
     "packageId",
     "identityId",
     "status",
     "requestedAt",
     "expiresAt",
     "packageName",
     "identityName"
    ],
    "additionalProperties": false
   },
   "PackageRequestStatus": {
    "type": "string",
    "enum": [
     "pending",
     "approved",
     "denied",
     "cancelled",
     "expired"
    ]
   },
   "AssignmentInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "packageId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "justification": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "packageId",
     "identityId"
    ],
    "additionalProperties": false
   },
   "PackageInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "groupIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "maxDurationMs": {
      "type": "number",
      "description": "Longest assignment the package allows (a minute to ten years); assignments must then state an end within it."
     },
     "requireJustification": {
      "type": "boolean",
      "description": "Assignments must state a justification."
     },
     "requestable": {
      "type": "boolean",
      "description": "Members holding iam:packages:request may ask for the package."
     },
     "approverGroupId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "Group whose members decide on requests (and are emailed them); null means anyone holding iam:packages:approve."
     },
     "managerApproval": {
      "type": "boolean",
      "description": "The requester's manager may decide (and is emailed each request)."
     },
     "autoAssign": {
      "$ref": "#/components/schemas/AutoAssignInput"
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "PublicPackage": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "groupIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "maxDurationMs": {
      "type": "number"
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requestable": {
      "type": "boolean"
     },
     "approverGroupId": {
      "type": "string"
     },
     "managerApproval": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "assignments": {
      "type": "number",
      "description": "Live holders."
     },
     "automaticAssignments": {
      "type": "number",
      "description": "Live holders the rule assigned."
     },
     "autoAssign": {
      "$ref": "#/components/schemas/PublicAutoAssign"
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "roleIds",
     "groupIds",
     "createdAt",
     "updatedAt",
     "assignments",
     "automaticAssignments"
    ],
    "additionalProperties": false
   },
   "PublicAutoAssign": {
    "type": "object",
    "properties": {
     "ownerName": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "suspended"
      ]
     },
     "suspendedReason": {
      "type": "string",
      "enum": [
       "invalid-rule",
       "owner-inactive",
       "authority-revoked",
       "owner-lacks-rights"
      ]
     },
     "suspendedDetail": {
      "type": "string"
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "issueCount": {
      "type": "number"
     },
     "issues": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "kind": {
         "type": "string",
         "enum": [
          "suspended",
          "failed",
          "braked"
         ]
        },
        "identityId": {
         "type": "string"
        },
        "identityName": {
         "type": "string"
        },
        "code": {
         "type": "string"
        },
        "message": {
         "type": "string"
        },
        "since": {
         "type": "number"
        }
       },
       "required": [
        "kind",
        "code",
        "message",
        "since"
       ],
       "additionalProperties": false
      },
      "description": "Newest first, at most 20."
     },
     "include": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      },
      "description": "1-10 clauses; an identity matches when any clause holds."
     },
     "exclude": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "StringEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotEqualsIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringNotLike": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "StringLikeIgnoreCase": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Bool": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericNotEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericLessThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThan": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NumericGreaterThanEquals": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateBefore": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "DateAfter": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "IpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "NotIpAddress": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContains": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "ArrayContainsAll": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        },
        "Exists": {
         "type": "object",
         "properties": {},
         "additionalProperties": {
          "anyOf": [
           {
            "type": "string"
           },
           {
            "type": "number"
           },
           {
            "type": "boolean",
            "const": false
           },
           {
            "type": "boolean",
            "const": true
           },
           {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/ConditionValue"
            }
           }
          ]
         }
        }
       },
       "additionalProperties": false
      },
      "description": "0-10 clauses; an identity matching any of them never matches (deny wins)."
     },
     "graceMs": {
      "type": "number",
      "description": "Automatic holders keep the package this long after they stop matching (at most 90 days); absent: removed at the next reconcile."
     },
     "maxGrants": {
      "type": "number",
      "description": "Unattended runs hold back this package's new grants (assign and refresh) above this count (default 100)."
     },
     "maxRemovals": {
      "type": "number",
      "description": "Unattended runs hold back this package's removals (revoke and grace starts) above this count (default 25)."
     },
     "ownerId": {
      "type": "string",
      "description": "The identity whose grant authority and rights the rule runs under."
     },
     "authorityId": {
      "type": "string",
      "description": "The owner's grant authority; every automatic binding is issued under it."
     },
     "revision": {
      "type": "number",
      "description": "Starts at 1; increments when the owner, the authority, or the package's roles or groups change."
     },
     "updatedAt": {
      "type": "number"
     },
     "approved": {
      "type": "object",
      "properties": {
       "grants": {
        "type": "number"
       },
       "removals": {
        "type": "number"
       },
       "until": {
        "type": "number"
       }
      },
      "required": [
       "grants",
       "removals",
       "until"
      ],
      "additionalProperties": false,
      "description": "Planned change counts a person approved (an API save or a confirmed reconcile); unattended runs may apply up to these until `until`."
     }
    },
    "required": [
     "ownerName",
     "status",
     "warnings",
     "issueCount",
     "issues",
     "include",
     "ownerId",
     "authorityId",
     "revision",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "PackageReconcileResult": {
    "type": "object",
    "properties": {
     "trigger": {
      "$ref": "#/components/schemas/ReconcileTrigger"
     },
     "assigned": {
      "type": "number"
     },
     "refreshed": {
      "type": "number"
     },
     "restored": {
      "type": "number"
     },
     "ending": {
      "type": "number"
     },
     "revoked": {
      "type": "number"
     },
     "stale": {
      "type": "number",
      "description": "Planned changes skipped because the state moved between planning and applying; retried next run."
     },
     "failed": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "packageId": {
         "type": "string"
        },
        "identityId": {
         "type": "string"
        },
        "change": {
         "$ref": "#/components/schemas/RuleChange"
        },
        "code": {
         "type": "string"
        },
        "message": {
         "type": "string"
        }
       },
       "required": [
        "tenantId",
        "packageId",
        "identityId",
        "change",
        "code",
        "message"
       ],
       "additionalProperties": false
      }
     },
     "suspended": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "packageId": {
         "type": "string"
        },
        "reason": {
         "$ref": "#/components/schemas/RuleSuspension"
        },
        "detail": {
         "type": "string"
        }
       },
       "required": [
        "tenantId",
        "packageId",
        "reason",
        "detail"
       ],
       "additionalProperties": false
      }
     },
     "braked": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "packageId": {
         "type": "string"
        },
        "direction": {
         "type": "string",
         "enum": [
          "grants",
          "removals"
         ]
        },
        "planned": {
         "type": "number"
        },
        "threshold": {
         "type": "number"
        }
       },
       "required": [
        "tenantId",
        "packageId",
        "direction",
        "planned",
        "threshold"
       ],
       "additionalProperties": false
      }
     },
     "skipped": {
      "type": "object",
      "properties": {
       "inactiveTenants": {
        "type": "number"
       },
       "failedTenants": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "tenantId": {
           "type": "string"
          },
          "code": {
           "type": "string"
          },
          "message": {
           "type": "string"
          }
         },
         "required": [
          "tenantId",
          "code",
          "message"
         ],
         "additionalProperties": false
        }
       }
      },
      "required": [
       "inactiveTenants",
       "failedTenants"
      ],
      "additionalProperties": false
     },
     "truncated": {
      "type": "boolean",
      "description": "The change budget ran out; run again."
     }
    },
    "required": [
     "trigger",
     "assigned",
     "refreshed",
     "restored",
     "ending",
     "revoked",
     "stale",
     "failed",
     "suspended",
     "braked",
     "skipped",
     "truncated"
    ],
    "additionalProperties": false
   },
   "ReconcileTrigger": {
    "type": "string",
    "enum": [
     "manual",
     "schedule",
     "identity-change",
     "rule-change"
    ]
   },
   "RuleChange": {
    "type": "string",
    "enum": [
     "revoke",
     "assign",
     "refresh",
     "restore",
     "ending"
    ]
   },
   "RuleSuspension": {
    "type": "string",
    "enum": [
     "invalid-rule",
     "owner-inactive",
     "authority-revoked",
     "owner-lacks-rights"
    ]
   },
   "PackageAssignmentSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "packageId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "assignedBy": {
      "type": "string"
     },
     "assignedAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "justification": {
      "type": "string"
     },
     "bindingIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "membershipIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "packageName": {
      "type": "string"
     },
     "identityName": {
      "type": "string"
     },
     "identityEmail": {
      "type": "string"
     },
     "expired": {
      "type": "boolean"
     },
     "broken": {
      "type": "boolean",
      "description": "Its bindings no longer grant (for example the assigner's authority was revoked); assign or request it again."
     },
     "automatic": {
      "type": "boolean",
      "description": "Assigned by the package rule."
     }
    },
    "required": [
     "id",
     "tenantId",
     "packageId",
     "identityId",
     "assignedBy",
     "assignedAt",
     "bindingIds",
     "membershipIds",
     "packageName",
     "identityName",
     "expired",
     "broken",
     "automatic"
    ],
    "additionalProperties": false
   },
   "AutoAssignPreview": {
    "type": "object",
    "properties": {
     "rule": {
      "type": "string",
      "enum": [
       "stored",
       "candidate",
       "none"
      ]
     },
     "keys": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/RuleKey"
      }
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "matching": {
      "type": "number"
     },
     "excluded": {
      "type": "number"
     },
     "frozen": {
      "type": "number"
     },
     "sample": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "identityId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "email": {
         "type": "string"
        },
        "kind": {
         "type": "string",
         "enum": [
          "user",
          "agent",
          "service"
         ]
        },
        "matchedBy": {
         "type": "array",
         "items": {
          "type": "string"
         }
        }
       },
       "required": [
        "identityId",
        "name",
        "kind",
        "matchedBy"
       ],
       "additionalProperties": false
      }
     },
     "plan": {
      "type": "object",
      "properties": {
       "assign": {
        "type": "number"
       },
       "refresh": {
        "type": "number"
       },
       "restore": {
        "type": "number"
       },
       "ending": {
        "type": "number"
       },
       "revoke": {
        "type": "number"
       },
       "manual": {
        "type": "number"
       },
       "keep": {
        "type": "number"
       }
      },
      "required": [
       "assign",
       "refresh",
       "restore",
       "ending",
       "revoke",
       "manual",
       "keep"
      ],
      "additionalProperties": false,
      "description": "Only with packageId."
     },
     "changes": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "identityId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "email": {
         "type": "string"
        },
        "change": {
         "$ref": "#/components/schemas/RuleChange"
        }
       },
       "required": [
        "identityId",
        "name",
        "change"
       ],
       "additionalProperties": false
      }
     },
     "brake": {
      "type": "object",
      "properties": {
       "grants": {
        "type": "object",
        "properties": {
         "planned": {
          "type": "number"
         },
         "threshold": {
          "type": "number"
         },
         "trips": {
          "type": "boolean"
         }
        },
        "required": [
         "planned",
         "threshold",
         "trips"
        ],
        "additionalProperties": false
       },
       "removals": {
        "type": "object",
        "properties": {
         "planned": {
          "type": "number"
         },
         "threshold": {
          "type": "number"
         },
         "trips": {
          "type": "boolean"
         }
        },
        "required": [
         "planned",
         "threshold",
         "trips"
        ],
        "additionalProperties": false
       }
      },
      "required": [
       "grants",
       "removals"
      ],
      "additionalProperties": false
     },
     "status": {
      "type": "object",
      "properties": {
       "status": {
        "type": "string",
        "enum": [
         "active",
         "suspended"
        ]
       },
       "reason": {
        "type": "string",
        "enum": [
         "invalid-rule",
         "owner-inactive",
         "authority-revoked",
         "owner-lacks-rights"
        ]
       },
       "detail": {
        "type": "string"
       }
      },
      "required": [
       "status"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "rule",
     "keys",
     "warnings",
     "matching",
     "excluded",
     "frozen",
     "sample"
    ],
    "additionalProperties": false
   },
   "RuleKey": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "type": {
      "$ref": "#/components/schemas/RuleKeyType"
     },
     "operators": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ConditionOperator"
      }
     }
    },
    "required": [
     "key",
     "type",
     "operators"
    ],
    "additionalProperties": false
   },
   "RuleKeyType": {
    "type": "string",
    "enum": [
     "string",
     "number",
     "boolean",
     "array"
    ]
   },
   "ConditionOperator": {
    "type": "string",
    "enum": [
     "StringEquals",
     "StringNotEquals",
     "StringEqualsIgnoreCase",
     "StringNotEqualsIgnoreCase",
     "StringLike",
     "StringNotLike",
     "StringLikeIgnoreCase",
     "Bool",
     "NumericEquals",
     "NumericNotEquals",
     "NumericLessThan",
     "NumericLessThanEquals",
     "NumericGreaterThan",
     "NumericGreaterThanEquals",
     "DateBefore",
     "DateAfter",
     "IpAddress",
     "NotIpAddress",
     "ArrayContains",
     "ArrayContainsAll",
     "Exists"
    ]
   },
   "PackageUpdate": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "null clears it."
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "groupIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "maxDurationMs": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ],
      "description": "null removes the cap."
     },
     "requireJustification": {
      "type": "boolean"
     },
     "requestable": {
      "type": "boolean"
     },
     "approverGroupId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "null clears the approver group."
     },
     "managerApproval": {
      "type": "boolean"
     },
     "autoAssign": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/AutoAssignInput"
       }
      ],
      "description": "Sets or replaces the rule (the caller becomes its owner); null removes it, and reconciliation removes its automatic assignments."
     },
     "keepAutomaticAssignments": {
      "type": "boolean",
      "description": "With autoAssign: null, turns the live automatic assignments into manual ones instead (at most 5000)."
     }
    },
    "additionalProperties": false
   },
   "PolicyInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     }
    },
    "required": [
     "tenantId",
     "name",
     "document"
    ],
    "additionalProperties": false
   },
   "Policy": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "version": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "document",
     "version",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "Decision": {
    "type": "object",
    "properties": {
     "allowed": {
      "type": "boolean"
     },
     "reason": {
      "type": "string"
     },
     "matched": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "allowed",
     "reason",
     "matched"
    ],
    "additionalProperties": false
   },
   "PolicyUpdate": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "policyId": {
      "type": "string"
     },
     "version": {
      "type": "number"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     }
    },
    "required": [
     "tenantId",
     "policyId",
     "version"
    ],
    "additionalProperties": false
   },
   "ReviewMatch": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ]
     },
     "reason": {
      "type": "string"
     }
    },
    "required": [
     "identityId",
     "name",
     "kind",
     "reason"
    ],
    "additionalProperties": false
   },
   "AccessReport": {
    "type": "object",
    "properties": {
     "generatedAt": {
      "type": "number"
     },
     "withinMs": {
      "type": "number"
     },
     "unusedForMs": {
      "type": "number"
     },
     "identities": {
      "type": "object",
      "properties": {
       "total": {
        "type": "number"
       },
       "disabled": {
        "type": "number"
       },
       "expiring": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/ExpiringIdentity"
        }
       }
      },
      "required": [
       "total",
       "disabled",
       "expiring"
      ],
      "additionalProperties": false
     },
     "bindings": {
      "type": "object",
      "properties": {
       "total": {
        "type": "number"
       },
       "eligible": {
        "type": "number"
       },
       "windowed": {
        "type": "number"
       },
       "expiring": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/ExpiringBinding"
        }
       },
       "starting": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/ExpiringBinding"
        },
        "description": "Future-dated bindings that start within the window."
       },
       "activations": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/LiveActivation"
        }
       },
       "pendingRequests": {
        "type": "number"
       },
       "expiringMemberships": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/ExpiringMembership"
        },
        "description": "Temporary group memberships ending within the window."
       }
      },
      "required": [
       "total",
       "eligible",
       "windowed",
       "expiring",
       "starting",
       "activations",
       "pendingRequests",
       "expiringMemberships"
      ],
      "additionalProperties": false
     },
     "credentials": {
      "type": "object",
      "properties": {
       "total": {
        "type": "number"
       },
       "unused": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/CredentialSummary"
        }
       },
       "expiring": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/CredentialSummary"
        }
       }
      },
      "required": [
       "total",
       "unused",
       "expiring"
      ],
      "additionalProperties": false
     },
     "omitted": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "bindings",
        "credentials"
       ]
      }
     }
    },
    "required": [
     "generatedAt",
     "withinMs",
     "unusedForMs",
     "identities",
     "omitted"
    ],
    "additionalProperties": false
   },
   "ExpiringIdentity": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ]
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     },
     "expiresAt": {
      "type": "number"
     },
     "expired": {
      "type": "boolean",
      "description": "Already past the deadline (refused, waiting for the retention worker)."
     }
    },
    "required": [
     "id",
     "name",
     "kind",
     "status",
     "expiresAt",
     "expired"
    ],
    "additionalProperties": false
   },
   "ExpiringBinding": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "roleName": {
      "type": "string"
     },
     "subjectType": {
      "type": "string",
      "enum": [
       "identity",
       "group"
      ]
     },
     "subjectId": {
      "type": "string"
     },
     "subjectName": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "eligible": {
      "type": "boolean"
     }
    },
    "required": [
     "id",
     "roleId",
     "subjectType",
     "subjectId",
     "expiresAt",
     "eligible"
    ],
    "additionalProperties": false
   },
   "LiveActivation": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "identityName": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "roleName": {
      "type": "string"
     },
     "activatedAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "justification": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "identityId",
     "roleId",
     "activatedAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "ExpiringMembership": {
    "type": "object",
    "properties": {
     "groupId": {
      "type": "string"
     },
     "groupName": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "identityName": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     }
    },
    "required": [
     "groupId",
     "identityId",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "CatalogResourceType": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "enum": [
       "platform",
       "tenant"
      ]
     },
     "description": {
      "type": "string"
     },
     "actions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "$ref": "#/components/schemas/AttributeType"
      }
     },
     "parent": {
      "type": "string"
     },
     "managed": {
      "type": "boolean"
     },
     "relations": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Relation names identities and groups may hold on resources of this type."
     }
    },
    "required": [
     "name",
     "source",
     "actions",
     "attributes",
     "managed",
     "relations"
    ],
    "additionalProperties": false
   },
   "ResourceTypeInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "actions": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Verbs; the registered actions are `{name}:{verb}`."
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "$ref": "#/components/schemas/AttributeType"
      }
     },
     "parent": {
      "type": "string"
     },
     "relations": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "ResourceTypeUpdate": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "$ref": "#/components/schemas/AttributeType"
      }
     },
     "actions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "relations": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "ResourceRecord": {
    "type": "object",
    "properties": {
     "type": {
      "type": "string"
     },
     "resourceId": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "parentType": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "ownerId": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "type",
     "resourceId",
     "attributes",
     "createdAt",
     "updatedAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "ResourceInput": {
    "type": "object",
    "properties": {
     "type": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      }
     },
     "parentId": {
      "type": "string"
     },
     "ownerId": {
      "type": "string"
     }
    },
    "required": [
     "type",
     "id"
    ],
    "additionalProperties": false
   },
   "RoleSuggestionKind": {
    "type": "string",
    "enum": [
     "bundle",
     "group-binding",
     "redundant-binding",
     "duplicate-roles"
    ]
   },
   "PeerOutlierResult": {
    "type": "object",
    "properties": {
     "generatedAt": {
      "type": "number"
     },
     "peerBy": {
      "type": "string"
     },
     "identitiesCompared": {
      "type": "number"
     },
     "outliers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeerOutlier"
      }
     }
    },
    "required": [
     "generatedAt",
     "peerBy",
     "identitiesCompared",
     "outliers"
    ],
    "additionalProperties": false
   },
   "PeerOutlier": {
    "type": "object",
    "properties": {
     "identity": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "peerValue": {
      "type": "string",
      "description": "The peer grouping: the manager's ID, or the attribute value."
     },
     "peers": {
      "type": "number"
     },
     "unusualRoles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeerRoleShare"
      }
     },
     "missingRoles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeerRoleShare"
      }
     }
    },
    "required": [
     "identity",
     "peerValue",
     "peers",
     "unusualRoles",
     "missingRoles"
    ],
    "additionalProperties": false
   },
   "MiningRef": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "name"
    ],
    "additionalProperties": false
   },
   "PeerRoleShare": {
    "type": "object",
    "properties": {
     "role": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "peersHolding": {
      "type": "number",
      "description": "How many peers (excluding the identity) hold the role."
     },
     "share": {
      "type": "number",
      "description": "peersHolding / peers, rounded to two decimals."
     }
    },
    "required": [
     "role",
     "peersHolding",
     "share"
    ],
    "additionalProperties": false
   },
   "ReviewRecommendationsResult": {
    "type": "object",
    "properties": {
     "campaignId": {
      "type": "string"
     },
     "unusedDays": {
      "type": "number"
     },
     "usageComplete": {
      "type": "boolean",
      "description": "Whether recorded usage covers the whole window (otherwise sign-ins are the evidence)."
     },
     "recommendations": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReviewRecommendation"
      }
     }
    },
    "required": [
     "campaignId",
     "unusedDays",
     "usageComplete",
     "recommendations"
    ],
    "additionalProperties": false
   },
   "ReviewRecommendation": {
    "type": "object",
    "properties": {
     "itemId": {
      "type": "string"
     },
     "recommendation": {
      "type": "string",
      "enum": [
       "keep",
       "revoke",
       "none"
      ]
     },
     "basis": {
      "type": "string",
      "enum": [
       "status",
       "usage",
       "sign-in"
      ],
      "description": "`usage`: recorded access usage; `sign-in`: last sign-in; `status`: the account is not active."
     },
     "reason": {
      "type": "string"
     },
     "lastUsedAt": {
      "type": "number"
     }
    },
    "required": [
     "itemId",
     "recommendation",
     "reason"
    ],
    "additionalProperties": false
   },
   "RightSizeResult": {
    "type": "object",
    "properties": {
     "generatedAt": {
      "type": "number"
     },
     "unusedDays": {
      "type": "number"
     },
     "tracking": {
      "type": "boolean",
      "description": "Whether the deployment records usage (`accessUsage` option)."
     },
     "trackingSince": {
      "type": "number",
      "description": "When this tenant's usage was first recorded."
     },
     "complete": {
      "type": "boolean",
      "description": "True once usage has been recorded for the whole window, so \"unused\" is conclusive."
     },
     "entries": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/RightSizeEntry"
      }
     },
     "roles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/RoleUsageSummary"
      }
     }
    },
    "required": [
     "generatedAt",
     "unusedDays",
     "tracking",
     "complete",
     "entries",
     "roles"
    ],
    "additionalProperties": false
   },
   "RightSizeEntry": {
    "type": "object",
    "properties": {
     "identity": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "role": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "bindingId": {
      "type": "string"
     },
     "via": {
      "anyOf": [
       {
        "type": "object",
        "properties": {
         "type": {
          "type": "string",
          "const": "identity"
         }
        },
        "required": [
         "type"
        ],
        "additionalProperties": false
       },
       {
        "type": "object",
        "properties": {
         "type": {
          "type": "string",
          "const": "group"
         },
         "id": {
          "type": "string"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "type",
         "id",
         "name"
        ],
        "additionalProperties": false
       }
      ],
      "description": "How the role reaches the person: their own binding or a group's."
     },
     "eligible": {
      "type": "boolean"
     },
     "status": {
      "type": "string",
      "enum": [
       "unused",
       "partial"
      ],
      "description": "`unused`: none of the role's actions used in the window; `partial`: some never used."
     },
     "grantedActions": {
      "type": "number"
     },
     "usedActions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "unusedActions": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The first 50 unused actions, sorted; `unusedCount` has the total."
     },
     "unusedCount": {
      "type": "number"
     },
     "lastUsedAt": {
      "type": "number"
     }
    },
    "required": [
     "identity",
     "role",
     "bindingId",
     "via",
     "eligible",
     "status",
     "grantedActions",
     "usedActions",
     "unusedActions",
     "unusedCount"
    ],
    "additionalProperties": false
   },
   "RoleUsageSummary": {
    "type": "object",
    "properties": {
     "role": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "holders": {
      "type": "number"
     },
     "grantedActions": {
      "type": "number"
     },
     "usedActions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "neverUsed": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The first 100 actions no holder used, sorted; `neverUsedCount` has the total."
     },
     "neverUsedCount": {
      "type": "number"
     }
    },
    "required": [
     "role",
     "holders",
     "grantedActions",
     "usedActions",
     "neverUsed",
     "neverUsedCount"
    ],
    "additionalProperties": false
   },
   "RoleMiningResult": {
    "type": "object",
    "properties": {
     "generatedAt": {
      "type": "number"
     },
     "summary": {
      "type": "object",
      "properties": {
       "bundle": {
        "type": "number"
       },
       "group-binding": {
        "type": "number"
       },
       "redundant-binding": {
        "type": "number"
       },
       "duplicate-roles": {
        "type": "number"
       }
      },
      "required": [
       "bundle",
       "group-binding",
       "redundant-binding",
       "duplicate-roles"
      ],
      "additionalProperties": false
     },
     "suggestions": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/RoleSuggestion"
      }
     }
    },
    "required": [
     "generatedAt",
     "summary",
     "suggestions"
    ],
    "additionalProperties": false
   },
   "RoleSuggestion": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "kind": {
      "$ref": "#/components/schemas/RoleSuggestionKind"
     },
     "title": {
      "type": "string"
     },
     "detail": {
      "type": "string"
     },
     "roles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/MiningRef"
      }
     },
     "identities": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/MiningRef"
      }
     },
     "group": {
      "$ref": "#/components/schemas/MiningRef"
     },
     "bindingIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Direct bindings the suggestion would remove (group-binding, redundant-binding)."
     },
     "savings": {
      "type": "number",
      "description": "Grants saved: assignments folded into one (bundle) or bindings removed net of those added."
     },
     "applicable": {
      "type": "boolean",
      "description": "Whether `roleMining.apply` can carry the suggestion out (bundles become packages through `packages.create`)."
     }
    },
    "required": [
     "id",
     "kind",
     "title",
     "detail",
     "roles",
     "identities",
     "savings",
     "applicable"
    ],
    "additionalProperties": false
   },
   "AssumeRoleInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string",
      "description": "The tenant of the role (the trust's tenant)."
     },
     "trustId": {
      "type": "string"
     },
     "externalId": {
      "type": "string",
      "description": "Required when the trust was created with an external ID."
     },
     "durationSeconds": {
      "type": "number",
      "description": "60 up to the trust's maximum (default 900, or less when the maximum is lower)."
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "sessionName": {
      "type": "string",
      "description": "A label for the session (/^[\\w+=,.@-]{2,64}$/), exposed as principal.sessionName and in audit events."
     },
     "sourceIdentity": {
      "type": "string",
      "description": "The person or workload behind the call, when the trust's sourceIdentityMode permits or requires it."
     },
     "tags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "Session tags (principal.sessionTags.{key}); every key must be admitted by the trust's allowedTagKeys."
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt",
       "opaque"
      ]
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs only: 1 to 5 audiences from `sts.jwt.audiences` (default: the IAM issuer)."
     }
    },
    "required": [
     "tenantId",
     "trustId"
    ],
    "additionalProperties": false
   },
   "RoleCredential": {
    "type": "object",
    "properties": {
     "session": {
      "allOf": [
       {
        "$ref": "#/components/schemas/TemporaryCredentialSession"
       },
       {
        "type": "object",
        "properties": {
         "roleId": {
          "type": "string"
         },
         "trustId": {
          "type": "string"
         }
        },
        "required": [
         "roleId",
         "trustId"
        ],
        "additionalProperties": false
       }
      ]
     },
     "token": {
      "type": "string"
     },
     "tokenType": {
      "type": "string",
      "const": "Bearer"
     },
     "format": {
      "$ref": "#/components/schemas/CredentialFormat"
     },
     "expiresAt": {
      "type": "number",
      "description": "Epoch milliseconds; never later than the source credential's expiry."
     },
     "expiresIn": {
      "type": "number",
      "description": "Seconds from issuance to `expiresAt`."
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs: the audiences the token names."
     }
    },
    "required": [
     "session",
     "token",
     "tokenType",
     "format",
     "expiresAt",
     "expiresIn"
    ],
    "additionalProperties": false
   },
   "TemporaryCredentialSession": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token",
       "delegated"
      ]
     },
     "identityId": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "mfa": {
      "type": "boolean"
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "sessionName": {
      "type": "string"
     },
     "sourceIdentity": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "tenantId",
     "kind",
     "identityId",
     "expiresAt",
     "mfa"
    ],
    "additionalProperties": false
   },
   "CredentialFormat": {
    "type": "string",
    "enum": [
     "jwt",
     "opaque"
    ]
   },
   "RoleInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "policyIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "permissions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "document": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "inherits": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Roles whose grants this role includes (at most 20, no cycles, no protected roles)."
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "BindingSubject": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ]
     }
    },
    "required": [
     "id",
     "name"
    ],
    "additionalProperties": false
   },
   "RoleSessionSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "sourceTenantId": {
      "type": "string"
     },
     "sessionName": {
      "type": "string"
     },
     "sourceIdentity": {
      "type": "string"
     },
     "webIdentity": {
      "type": "object",
      "properties": {
       "providerId": {
        "type": "string"
       },
       "subject": {
        "type": "string"
       }
      },
      "required": [
       "providerId",
       "subject"
      ],
      "additionalProperties": false
     },
     "mfa": {
      "type": "boolean"
     },
     "format": {
      "$ref": "#/components/schemas/CredentialFormat"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "clientIp": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "roleId",
     "trustId",
     "identityId",
     "mfa",
     "format",
     "createdAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "RoleUpdate": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "policyIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "permissions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "document": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/PolicyDocument"
       }
      ]
     },
     "inherits": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Replaces the inherited roles; an empty list clears inheritance."
     }
    },
    "required": [
     "tenantId",
     "roleId"
    ],
    "additionalProperties": false
   },
   "Identity": {
    "type": "object",
    "properties": {
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ],
      "description": "`user` (a person), `service` (a service account) or `agent` (an AI agent with a sponsor, see `agent`)."
     },
     "agent": {
      "$ref": "#/components/schemas/AgentProfile"
     },
     "email": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "attributes": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "description": "Any JSON value"
      },
      "description": "Typed attributes declared by `permissions.identityAttributes`, exposed to policies as principal.{name}."
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ],
      "description": "deleted identities are tombstones: no credentials, factors, bindings, or email, kept so audit records stay resolvable."
     },
     "emailVerified": {
      "type": "boolean"
     },
     "rootAdmin": {
      "type": "boolean"
     },
     "owner": {
      "type": "boolean"
     },
     "createdAt": {
      "type": "number"
     },
     "passwordHash": {
      "type": "string"
     },
     "passwordChangedAt": {
      "type": "number",
      "description": "When the current password was set; drives `passwordMaxAgeDays` (identities without it use `createdAt`)."
     },
     "phone": {
      "type": "string"
     },
     "phoneVerified": {
      "type": "boolean"
     },
     "expiresAt": {
      "type": "number",
      "description": "Scheduled deactivation (epoch milliseconds) for contractors and temporary service accounts: past this time the\nidentity's credentials are refused and the retention worker disables it (`identity:expire`)."
     },
     "managerId": {
      "type": "string",
      "description": "The person's manager within the tenant: approvals may route to them, and offboarding hands reports to a successor."
     },
     "deletedAt": {
      "type": "number"
     },
     "deletedEmail": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "kind",
     "name",
     "status",
     "emailVerified",
     "rootAdmin",
     "owner",
     "createdAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "SodRule": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "roleIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "mode": {
      "type": "string",
      "enum": [
       "prevent",
       "detect"
      ]
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "roleIds",
     "mode",
     "createdAt",
     "createdBy",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "WebIdentityExchangeInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string",
      "description": "The tenant of the trust (and of the role)."
     },
     "trustId": {
      "type": "string"
     },
     "webIdentityToken": {
      "type": "string",
      "description": "The external OIDC ID token (a compact JWS of at most 8192 characters)."
     },
     "sessionName": {
      "type": "string",
      "description": "Required: a label for the session (/^[\\w+=,.@-]{2,64}$/), exposed as principal.sessionName."
     },
     "durationSeconds": {
      "type": "number",
      "description": "60 up to the trust's maximum (default 900, or less when the maximum is lower)."
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt",
       "opaque"
      ]
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs only: 1 to 5 audiences from `sts.jwt.audiences` (default: the IAM issuer)."
     }
    },
    "required": [
     "tenantId",
     "trustId",
     "webIdentityToken",
     "sessionName"
    ],
    "additionalProperties": false
   },
   "WebIdentityCredential": {
    "type": "object",
    "properties": {
     "webIdentity": {
      "type": "object",
      "properties": {
       "providerId": {
        "type": "string"
       },
       "issuer": {
        "type": "string"
       },
       "subject": {
        "type": "string"
       }
      },
      "required": [
       "providerId",
       "issuer",
       "subject"
      ],
      "additionalProperties": false
     },
     "session": {
      "allOf": [
       {
        "$ref": "#/components/schemas/TemporaryCredentialSession"
       },
       {
        "type": "object",
        "properties": {
         "roleId": {
          "type": "string"
         },
         "trustId": {
          "type": "string"
         }
        },
        "required": [
         "roleId",
         "trustId"
        ],
        "additionalProperties": false
       }
      ]
     },
     "token": {
      "type": "string"
     },
     "tokenType": {
      "type": "string",
      "const": "Bearer"
     },
     "format": {
      "$ref": "#/components/schemas/CredentialFormat"
     },
     "expiresAt": {
      "type": "number",
      "description": "Epoch milliseconds; never later than the source credential's expiry."
     },
     "expiresIn": {
      "type": "number",
      "description": "Seconds from issuance to `expiresAt`."
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs: the audiences the token names."
     }
    },
    "required": [
     "webIdentity",
     "session",
     "token",
     "tokenType",
     "format",
     "expiresAt",
     "expiresIn"
    ],
    "additionalProperties": false
   },
   "CallerIdentity": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "identityTenantId": {
      "type": "string"
     },
     "identityKind": {
      "type": "string",
      "enum": [
       "user",
       "agent",
       "service"
      ]
     },
     "tenantId": {
      "type": "string",
      "description": "The tenant the session acts in (the role's tenant for role sessions)."
     },
     "sessionId": {
      "type": "string"
     },
     "sessionKind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token",
       "delegated"
      ]
     },
     "format": {
      "$ref": "#/components/schemas/CredentialFormat"
     },
     "mfa": {
      "type": "boolean"
     },
     "authenticatedAt": {
      "type": "number"
     },
     "issuedAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "method": {
      "type": "string",
      "enum": [
       "password",
       "passwordless-email",
       "passwordless-sms",
       "passkey",
       "federated",
       "impersonation"
      ]
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "sourceTenantId": {
      "type": "string"
     },
     "sessionName": {
      "type": "string"
     },
     "sourceIdentity": {
      "type": "string"
     },
     "sessionTags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      }
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "webIdentity": {
      "type": "object",
      "properties": {
       "providerId": {
        "type": "string"
       },
       "issuer": {
        "type": "string"
       },
       "subject": {
        "type": "string"
       }
      },
      "required": [
       "providerId",
       "issuer",
       "subject"
      ],
      "additionalProperties": false
     },
     "impersonatorId": {
      "type": "string"
     },
     "agentId": {
      "type": "string",
      "description": "Delegated sessions: the agent acting for the identity, and the delegation it acts under."
     },
     "delegationId": {
      "type": "string"
     }
    },
    "required": [
     "identityId",
     "identityTenantId",
     "identityKind",
     "tenantId",
     "sessionId",
     "sessionKind",
     "format",
     "mfa",
     "authenticatedAt",
     "issuedAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "GetSessionTokenInput": {
    "type": "object",
    "properties": {
     "durationSeconds": {
      "type": "number"
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "sessionName": {
      "type": "string"
     },
     "mfaCode": {
      "type": "string",
      "description": "A current TOTP code: the token then carries a fresh MFA time (user sources with TOTP only)."
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt",
       "opaque"
      ]
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "additionalProperties": false
   },
   "TemporaryCredential": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string"
     },
     "tokenType": {
      "type": "string",
      "const": "Bearer"
     },
     "format": {
      "$ref": "#/components/schemas/CredentialFormat"
     },
     "expiresAt": {
      "type": "number",
      "description": "Epoch milliseconds; never later than the source credential's expiry."
     },
     "expiresIn": {
      "type": "number",
      "description": "Seconds from issuance to `expiresAt`."
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs: the audiences the token names."
     },
     "session": {
      "$ref": "#/components/schemas/TemporaryCredentialSession"
     }
    },
    "required": [
     "token",
     "tokenType",
     "format",
     "expiresAt",
     "expiresIn",
     "session"
    ],
    "additionalProperties": false
   },
   "TeamMember": {
    "type": "object",
    "properties": {
     "teamId": {
      "type": "string"
     },
     "identityId": {
      "type": "string"
     },
     "role": {
      "$ref": "#/components/schemas/TeamRole"
     },
     "addedAt": {
      "type": "number"
     },
     "addedBy": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number",
      "description": "Temporary membership: past this time the person no longer belongs (the backing group membership ends with it)."
     },
     "source": {
      "type": "string",
      "enum": [
       "sync"
      ],
      "description": "`sync` when team sync added the person from a source group (then it also removes them); absent for manual members."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "teamId",
     "identityId",
     "role",
     "addedAt",
     "addedBy",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "TeamRole": {
    "type": "string",
    "enum": [
     "member",
     "maintainer"
    ]
   },
   "TeamJoinRequestView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "team": {
      "$ref": "#/components/schemas/TeamRef"
     },
     "requester": {
      "$ref": "#/components/schemas/TeamPerson"
     },
     "status": {
      "$ref": "#/components/schemas/TeamJoinRequestStatus"
     },
     "requestedAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "justification": {
      "type": "string"
     },
     "decidedBy": {
      "type": "string"
     },
     "decidedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "team",
     "requester",
     "status",
     "requestedAt",
     "expiresAt"
    ],
    "additionalProperties": false
   },
   "TeamRef": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     }
    },
    "required": [
     "id",
     "name",
     "slug"
    ],
    "additionalProperties": false
   },
   "TeamPerson": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     }
    },
    "required": [
     "id",
     "name",
     "status"
    ],
    "additionalProperties": false
   },
   "TeamJoinRequestStatus": {
    "type": "string",
    "enum": [
     "pending",
     "approved",
     "denied",
     "cancelled",
     "expired"
    ]
   },
   "TeamReviewView": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "team": {
      "$ref": "#/components/schemas/TeamRef"
     },
     "status": {
      "$ref": "#/components/schemas/TeamReviewStatus"
     },
     "onUndecided": {
      "$ref": "#/components/schemas/TeamReviewDecision"
     },
     "startedAt": {
      "type": "number"
     },
     "startedBy": {
      "$ref": "#/components/schemas/TeamPerson"
     },
     "dueAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     },
     "completedAt": {
      "type": "number"
     },
     "completedBy": {
      "type": "string"
     },
     "outcome": {
      "$ref": "#/components/schemas/TeamReviewOutcome"
     },
     "counts": {
      "type": "object",
      "properties": {
       "total": {
        "type": "number"
       },
       "keep": {
        "type": "number"
       },
       "remove": {
        "type": "number"
       },
       "undecided": {
        "type": "number"
       }
      },
      "required": [
       "total",
       "keep",
       "remove",
       "undecided"
      ],
      "additionalProperties": false
     },
     "items": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TeamReviewItemView"
      }
     }
    },
    "required": [
     "id",
     "team",
     "status",
     "onUndecided",
     "startedAt",
     "startedBy",
     "dueAt",
     "counts"
    ],
    "additionalProperties": false
   },
   "TeamReviewStatus": {
    "type": "string",
    "enum": [
     "cancelled",
     "open",
     "completed"
    ]
   },
   "TeamReviewDecision": {
    "type": "string",
    "enum": [
     "keep",
     "remove"
    ]
   },
   "TeamReviewOutcome": {
    "type": "object",
    "properties": {
     "kept": {
      "type": "number"
     },
     "removed": {
      "type": "number"
     },
     "undecided": {
      "type": "number",
      "description": "People nobody decided on (they followed `onUndecided`)."
     },
     "gone": {
      "type": "number",
      "description": "People who had already left the team when the review completed."
     }
    },
    "required": [
     "kept",
     "removed",
     "undecided",
     "gone"
    ],
    "additionalProperties": false
   },
   "TeamReviewItemView": {
    "type": "object",
    "properties": {
     "person": {
      "$ref": "#/components/schemas/TeamPerson"
     },
     "role": {
      "$ref": "#/components/schemas/TeamRole"
     },
     "decision": {
      "type": "string",
      "enum": [
       "keep",
       "remove"
      ]
     },
     "decidedBy": {
      "$ref": "#/components/schemas/TeamPerson"
     },
     "decidedAt": {
      "type": "number"
     },
     "note": {
      "type": "string"
     }
    },
    "required": [
     "person",
     "role"
    ],
    "additionalProperties": false
   },
   "TeamInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string",
      "description": "Defaults to one derived from the name."
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "departmentId": {
      "type": "string"
     },
     "joinPolicy": {
      "type": "string",
      "enum": [
       "closed",
       "request"
      ]
     },
     "memberManagement": {
      "type": "string",
      "enum": [
       "maintainers",
       "admins"
      ]
     },
     "maintainerIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Up to 20 people made maintainers of the new team."
     },
     "syncGroupIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Team sync: up to 10 ordinary groups whose members are kept as members of the team."
     }
    },
    "required": [
     "tenantId",
     "name"
    ],
    "additionalProperties": false
   },
   "TeamDetail": {
    "type": "object",
    "properties": {
     "path": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TeamRef"
      },
      "description": "Teams above this one, top first."
     },
     "children": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/TeamRef"
        },
        {
         "type": "object",
         "properties": {
          "memberCount": {
           "type": "number"
          }
         },
         "required": [
          "memberCount"
         ],
         "additionalProperties": false
        }
       ]
      }
     },
     "department": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "name": {
        "type": "string"
       },
       "code": {
        "type": "string"
       }
      },
      "required": [
       "id",
       "name"
      ],
      "additionalProperties": false
     },
     "maintainers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TeamPerson"
      }
     },
     "totalMemberCount": {
      "type": "number",
      "description": "Everyone in the backing group: members of this team and of every team below it."
     },
     "roles": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TeamRoleGrant"
      }
     },
     "syncGroups": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "name": {
         "type": "string"
        }
       },
       "required": [
        "id",
        "name"
       ],
       "additionalProperties": false
      },
      "description": "The groups team sync keeps the membership in step with."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "departmentId": {
      "type": "string"
     },
     "groupId": {
      "type": "string"
     },
     "joinPolicy": {
      "$ref": "#/components/schemas/TeamJoinPolicy"
     },
     "memberManagement": {
      "$ref": "#/components/schemas/TeamMemberManagement"
     },
     "syncGroupIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Team sync sources, when the team syncs its members from groups."
     },
     "memberCount": {
      "type": "number",
      "description": "Live direct members."
     },
     "maintainerCount": {
      "type": "number"
     },
     "childCount": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "path",
     "children",
     "maintainers",
     "totalMemberCount",
     "roles",
     "syncGroups",
     "id",
     "tenantId",
     "name",
     "slug",
     "groupId",
     "joinPolicy",
     "memberManagement",
     "memberCount",
     "maintainerCount",
     "childCount",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "TeamRoleGrant": {
    "type": "object",
    "properties": {
     "bindingId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "roleName": {
      "type": "string"
     },
     "team": {
      "$ref": "#/components/schemas/TeamRef"
     },
     "inherited": {
      "type": "boolean",
      "description": "True when the binding belongs to a team above this one."
     },
     "eligible": {
      "type": "boolean"
     },
     "startsAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     }
    },
    "required": [
     "bindingId",
     "roleId",
     "roleName",
     "team",
     "inherited"
    ],
    "additionalProperties": false
   },
   "TeamJoinPolicy": {
    "type": "string",
    "enum": [
     "closed",
     "request"
    ]
   },
   "TeamMemberManagement": {
    "type": "string",
    "enum": [
     "maintainers",
     "admins"
    ]
   },
   "TeamSummary": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "parentId": {
      "type": "string"
     },
     "departmentId": {
      "type": "string"
     },
     "groupId": {
      "type": "string"
     },
     "joinPolicy": {
      "$ref": "#/components/schemas/TeamJoinPolicy"
     },
     "memberManagement": {
      "$ref": "#/components/schemas/TeamMemberManagement"
     },
     "syncGroupIds": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Team sync sources, when the team syncs its members from groups."
     },
     "memberCount": {
      "type": "number",
      "description": "Live direct members."
     },
     "maintainerCount": {
      "type": "number"
     },
     "childCount": {
      "type": "number"
     },
     "createdAt": {
      "type": "number"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "tenantId",
     "name",
     "slug",
     "groupId",
     "joinPolicy",
     "memberManagement",
     "memberCount",
     "maintainerCount",
     "childCount",
     "createdAt",
     "updatedAt"
    ],
    "additionalProperties": false
   },
   "TeamMemberView": {
    "type": "object",
    "properties": {
     "role": {
      "$ref": "#/components/schemas/TeamRole"
     },
     "team": {
      "$ref": "#/components/schemas/TeamRef"
     },
     "addedAt": {
      "type": "number"
     },
     "addedBy": {
      "type": "string"
     },
     "expiresAt": {
      "type": "number"
     },
     "source": {
      "type": "string",
      "enum": [
       "sync"
      ],
      "description": "`sync` for members team sync manages (they come and go with the source groups)."
     },
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "email": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "disabled",
       "deleted"
      ]
     }
    },
    "required": [
     "role",
     "team",
     "addedAt",
     "addedBy",
     "id",
     "name",
     "status"
    ],
    "additionalProperties": false
   },
   "MyTeams": {
    "type": "object",
    "properties": {
     "teams": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/TeamRef"
        },
        {
         "type": "object",
         "properties": {
          "description": {
           "type": "string"
          },
          "role": {
           "$ref": "#/components/schemas/TeamRole"
          },
          "expiresAt": {
           "type": "number"
          },
          "parents": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/TeamRef"
           },
           "description": "Teams above it, whose access the membership also brings."
          }
         },
         "required": [
          "role",
          "parents"
         ],
         "additionalProperties": false
        }
       ]
      }
     },
     "requests": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TeamJoinRequestView"
      }
     },
     "joinable": {
      "type": "array",
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/TeamRef"
        },
        {
         "type": "object",
         "properties": {
          "description": {
           "type": "string"
          },
          "memberCount": {
           "type": "number"
          }
         },
         "required": [
          "memberCount"
         ],
         "additionalProperties": false
        }
       ]
      },
      "description": "Teams that take join requests and that the person is not in."
     },
     "reviews": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "team": {
         "$ref": "#/components/schemas/TeamRef"
        },
        "dueAt": {
         "type": "number"
        },
        "undecided": {
         "type": "number"
        }
       },
       "required": [
        "id",
        "team",
        "dueAt",
        "undecided"
       ],
       "additionalProperties": false
      },
      "description": "Open membership reviews of teams the person maintains (directly or through a team above)."
     }
    },
    "required": [
     "teams",
     "requests",
     "joinable",
     "reviews"
    ],
    "additionalProperties": false
   },
   "TeamSyncResult": {
    "type": "object",
    "properties": {
     "added": {
      "type": "number"
     },
     "removed": {
      "type": "number"
     },
     "updated": {
      "type": "number"
     }
    },
    "required": [
     "added",
     "removed",
     "updated"
    ],
    "additionalProperties": false
   },
   "TeamGroupSyncResult": {
    "type": "object",
    "properties": {
     "added": {
      "type": "number"
     },
     "removed": {
      "type": "number"
     },
     "updated": {
      "type": "number"
     },
     "teams": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Teams whose membership changed."
     }
    },
    "required": [
     "added",
     "removed",
     "updated",
     "teams"
    ],
    "additionalProperties": false
   },
   "TeamUpdate": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "teamId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "slug": {
      "type": "string"
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "An empty string or null clears it."
     },
     "parentId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ],
      "description": "null makes it a top-level team."
     },
     "departmentId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "joinPolicy": {
      "type": "string",
      "enum": [
       "closed",
       "request"
      ]
     },
     "memberManagement": {
      "type": "string",
      "enum": [
       "maintainers",
       "admins"
      ]
     },
     "syncGroupIds": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ],
      "description": "Team sync sources; null or an empty list stops syncing and removes the synced members."
     }
    },
    "required": [
     "tenantId",
     "teamId"
    ],
    "additionalProperties": false
   },
   "Tenant": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "type": {
      "type": "string"
     },
     "parentId": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "status": {
      "$ref": "#/components/schemas/TenantStatus"
     },
     "slug": {
      "type": "string",
      "description": "Optional globally unique sign-in alias, like an AWS account alias."
     },
     "region": {
      "type": "string",
      "description": "Home region, when the deployment runs in several regions (`regions` option). Unset means the parent's region;\nsign-in for a tenant homed elsewhere is sent to that region's deployment."
     },
     "boundary": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "authPolicy": {
      "$ref": "#/components/schemas/TenantAuthPolicy"
     },
     "accessPolicy": {
      "$ref": "#/components/schemas/TenantAccessPolicy"
     },
     "limits": {
      "$ref": "#/components/schemas/TenantLimits"
     },
     "createdAt": {
      "type": "number"
     },
     "deletedAt": {
      "type": "number",
      "description": "Set when a tenant is tombstoned; purge workers remove data after the retention window."
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "name",
     "type",
     "parentId",
     "status",
     "createdAt",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "TenantAuthPolicy": {
    "type": "object",
    "properties": {
     "requireMfa": {
      "type": "boolean",
      "description": "Every human sign-in must complete MFA; people without a factor enroll on their next sign-in."
     },
     "allowedMethods": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AuthMethod"
      },
      "description": "Sign-in methods the tenant accepts; unset accepts every method the deployment enables."
     },
     "sessionLifetimeMs": {
      "type": "number",
      "description": "Absolute session lifetime, capped by the deployment's `sessionLifetimeMs`."
     },
     "sessionIdleTimeoutMs": {
      "type": "number",
      "description": "Idle timeout, capped by the deployment's `sessionIdleTimeoutMs` and by the lifetime."
     },
     "maxAttempts": {
      "type": "number",
      "description": "Attempts per rate-limit window for this tenant's authentication flows, never above the deployment's limits."
     },
     "minPasswordLength": {
      "type": "number",
      "description": "Minimum password length for people in this tenant (12 to 128; the deployment minimum is 12)."
     },
     "maxSessions": {
      "type": "number",
      "description": "Concurrent user sessions per person (1 to 100); issuing one more ends the oldest."
     },
     "allowImpersonation": {
      "type": "boolean",
      "description": "Lets administrators holding `iam:identities:impersonate` open sessions as members (\"view as\"); off by default."
     },
     "trustedDeviceDays": {
      "type": "number",
      "description": "How long a remembered device may skip MFA (0 to 365 days; 0 disables); never longer than the deployment allows."
     },
     "passwordHistory": {
      "type": "number",
      "description": "Reject reuse of this many most recent passwords, counting the current one (1 to 24)."
     },
     "passwordMaxAgeDays": {
      "type": "number",
      "description": "Passwords older than this many days stop signing in (PASSWORD_EXPIRED) until reset (1 to 3650)."
     },
     "passwordMinClasses": {
      "type": "number",
      "description": "Character classes (lowercase, uppercase, digits, symbols) a new password must mix (2 to 4)."
     },
     "passwordRejectPersonalInfo": {
      "type": "boolean",
      "description": "Reject passwords that contain the person's email local part or a word of their name."
     },
     "notifyNewSignIn": {
      "type": "boolean",
      "description": "Email people when a session starts from a client no live session or remembered device of theirs has used; overrides the deployment default."
     },
     "allowedIpRanges": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Networks (IPv4/IPv6 addresses or CIDR blocks) sessions may be issued from and used from; sign-ins and sessions\nwhose recorded client IP falls outside them are refused with IP_NOT_ALLOWED. Needs `http.clientInfo` to record IPs."
     },
     "mfaEmailCodes": {
      "type": "boolean",
      "description": "Let people without an authenticator satisfy the MFA requirement with a one-time code emailed to their address\n(`auth.requestMfaCode`); overrides the deployment default. Root administrators always need an authenticator."
     },
     "requireMfaForOwners": {
      "type": "boolean",
      "description": "Owners must complete MFA even when the rest of the tenant need not (`requireMfa` covers everyone)."
     },
     "bindSessionsToIp": {
      "type": "boolean",
      "description": "A user session is usable only from the client IP it was issued from; used elsewhere it is refused with\nSESSION_NETWORK_MISMATCH (the person signs in again from the new network, the old session keeps working from the\nold one). Needs recorded client IPs (`http.clientInfo`); sessions or requests without one are not judged."
     }
    },
    "additionalProperties": false
   },
   "TenantLimits": {
    "type": "object",
    "properties": {
     "identities": {
      "type": "number",
      "description": "Active and disabled people (deleted tombstones do not count)."
     },
     "serviceAccounts": {
      "type": "number"
     },
     "agents": {
      "type": "number",
      "description": "AI agents (identities of kind `agent`, deleted ones excluded)."
     },
     "groups": {
      "type": "number"
     },
     "roles": {
      "type": "number"
     },
     "policies": {
      "type": "number"
     },
     "resources": {
      "type": "number",
      "description": "Registered managed resources."
     },
     "webhooks": {
      "type": "number"
     }
    },
    "additionalProperties": false
   },
   "TenantDiscovery": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "type": {
      "type": "string"
     },
     "slug": {
      "type": "string",
      "description": "The organization's alias; empty only for an organization without one, found by its custom hostname."
     },
     "region": {
      "type": "string",
      "description": "Its home region, in a multi-region deployment."
     },
     "signInUrl": {
      "type": "string",
      "description": "Its canonical sign-in URL, when organization addresses or regions are configured."
     }
    },
    "required": [
     "tenantId",
     "name",
     "type",
     "slug"
    ],
    "additionalProperties": false
   },
   "TrustCreateInput": {
    "anyOf": [
     {
      "$ref": "#/components/schemas/IdentityTrustCreateInput"
     },
     {
      "$ref": "#/components/schemas/WebIdentityTrustCreateInput"
     }
    ]
   },
   "IdentityTrustCreateInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string",
      "description": "The target tenant: the tenant of the role."
     },
     "kind": {
      "type": "string",
      "enum": [
       "identity"
      ],
      "description": "'identity' (default)."
     },
     "sourceTenantId": {
      "type": "string"
     },
     "sourceIdentityId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "requireMfa": {
      "type": "boolean",
      "description": "The source session must be MFA-verified (default true)."
     },
     "externalId": {
      "type": "string",
      "description": "A shared secret the caller must present on `roles.assume`; only its SHA-256 is stored."
     },
     "ceiling": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "maxSessionSeconds": {
      "type": "number",
      "description": "Longest role session, 60 up to `sts.maxRoleSessionSeconds` (default 3600)."
     },
     "passSourceAttributes": {
      "type": "boolean",
      "description": "Pass the source identity's attributes into role sessions (default: only for same-tenant trusts)."
     },
     "allowedTagKeys": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session tag keys callers may set (at most 50), or exactly ['*']; default none."
     },
     "sourceIdentityMode": {
      "type": "string",
      "enum": [
       "required",
       "forbidden",
       "optional"
      ],
      "description": "Whether callers may or must state a source identity; default 'forbidden'."
     },
     "description": {
      "type": "string",
      "description": "At most 512 characters."
     }
    },
    "required": [
     "tenantId",
     "sourceTenantId",
     "sourceIdentityId",
     "roleId"
    ],
    "additionalProperties": false
   },
   "WebIdentityTrustCreateInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string",
      "description": "The tenant of the role, the provider and the service account."
     },
     "kind": {
      "type": "string",
      "const": "web-identity"
     },
     "providerId": {
      "type": "string",
      "description": "The OIDC provider whose tokens the trust admits (in the same tenant)."
     },
     "serviceAccountId": {
      "type": "string",
      "description": "The active service account sessions act as (stored as `sourceIdentityId`)."
     },
     "roleId": {
      "type": "string"
     },
     "conditions": {
      "type": "object",
      "properties": {
       "StringEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLikeIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Bool": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateBefore": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateAfter": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "IpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NotIpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContains": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContainsAll": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Exists": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       }
      },
      "additionalProperties": false,
      "description": "Claim conditions: a policy statement's `conditions` block over `token.<claim>` keys (at most 20 entries). It must\npin `token.sub` with StringEquals or StringLike and no leading wildcard (else WEAK_TRUST_CONDITIONS)."
     },
     "tagClaims": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "Session tag key to flattened claim name (`token.<claim>`), at most 10."
     },
     "sourceIdentityClaim": {
      "type": "string",
      "description": "The flattened claim that becomes the session's source identity; a token without a valid one is refused."
     },
     "maxSessionSeconds": {
      "type": "number",
      "description": "Longest role session, 60 up to `sts.maxRoleSessionSeconds` (default 3600)."
     },
     "ceiling": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "passSourceAttributes": {
      "type": "boolean",
      "description": "Pass the service account's attributes into role sessions (default true)."
     },
     "description": {
      "type": "string",
      "description": "At most 512 characters."
     }
    },
    "required": [
     "tenantId",
     "kind",
     "providerId",
     "serviceAccountId",
     "roleId",
     "conditions"
    ],
    "additionalProperties": false
   },
   "PublicTrust": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "kind": {
      "type": "string",
      "enum": [
       "identity",
       "web-identity"
      ],
      "description": "'identity' for legacy records."
     },
     "sourceTenantId": {
      "type": "string"
     },
     "sourceIdentityId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "requireMfa": {
      "type": "boolean"
     },
     "requiresExternalId": {
      "type": "boolean",
      "description": "The caller must present an external ID."
     },
     "revoked": {
      "type": "boolean"
     },
     "ceiling": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "maxSessionSeconds": {
      "type": "number"
     },
     "passSourceAttributes": {
      "type": "boolean"
     },
     "allowedTagKeys": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "sourceIdentityMode": {
      "type": "string",
      "enum": [
       "required",
       "forbidden",
       "optional"
      ]
     },
     "sessionsRevokedBefore": {
      "type": "number"
     },
     "providerId": {
      "type": "string"
     },
     "conditions": {
      "type": "object",
      "properties": {
       "StringEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLikeIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Bool": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateBefore": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateAfter": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "IpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NotIpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContains": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContainsAll": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Exists": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       }
      },
      "additionalProperties": false
     },
     "tagClaims": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      }
     },
     "sourceIdentityClaim": {
      "type": "string"
     },
     "authorityId": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "createdBy": {
      "type": "string"
     },
     "updatedAt": {
      "type": "number"
     }
    },
    "required": [
     "id",
     "tenantId",
     "kind",
     "sourceTenantId",
     "sourceIdentityId",
     "roleId",
     "requireMfa",
     "requiresExternalId",
     "revoked"
    ],
    "additionalProperties": false
   },
   "WebIdentityEvaluation": {
    "type": "object",
    "properties": {
     "verified": {
      "type": "boolean",
      "description": "Whether the signature, issuer, audience and time claims verified against the provider."
     },
     "reason": {
      "type": "string",
      "enum": [
       "expired",
       "role",
       "trust",
       "audience",
       "conditions",
       "malformed",
       "type",
       "algorithm",
       "unknown-key",
       "signature",
       "issuer",
       "not-yet-valid",
       "too-old",
       "lifetime",
       "claims",
       "jwks-unavailable",
       "source-identity",
       "provider",
       "service-account",
       "authority"
      ],
      "description": "Set when the exchange would refuse the token."
     },
     "claims": {
      "type": "object",
      "properties": {
       "iss": {
        "type": "string"
       },
       "sub": {
        "type": "string"
       },
       "aud": {
        "type": "string"
       },
       "iat": {
        "type": "number"
       },
       "exp": {
        "type": "number"
       },
       "jti": {
        "type": "string"
       }
      },
      "required": [
       "iss",
       "sub",
       "aud",
       "iat",
       "exp"
      ],
      "additionalProperties": false,
      "description": "The verified registered claims; `aud` is the provider audience the token was accepted for."
     },
     "conditions": {
      "type": "object",
      "properties": {
       "matched": {
        "type": "boolean"
       },
       "failed": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      },
      "required": [
       "matched",
       "failed"
      ],
      "additionalProperties": false,
      "description": "The trust conditions; `failed` lists each unmet entry as `Operator:key`."
     },
     "sessionTags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "The session tags the trust's `tagClaims` would map."
     },
     "sourceIdentity": {
      "type": "string",
      "description": "The source identity the trust's `sourceIdentityClaim` would map."
     }
    },
    "required": [
     "verified"
    ],
    "additionalProperties": false
   },
   "TrustRevokeSessionsInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "before": {
      "type": "number",
      "description": "Epoch milliseconds, no later than now + 1."
     }
    },
    "required": [
     "tenantId",
     "trustId"
    ],
    "additionalProperties": false
   },
   "TrustUpdateInput": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "requireMfa": {
      "type": "boolean"
     },
     "ceiling": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/PolicyDocument"
       }
      ]
     },
     "maxSessionSeconds": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "passSourceAttributes": {
      "type": "boolean"
     },
     "allowedTagKeys": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "sourceIdentityMode": {
      "type": "string",
      "enum": [
       "required",
       "forbidden",
       "optional"
      ]
     },
     "description": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     },
     "conditions": {
      "type": "object",
      "properties": {
       "StringEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotEqualsIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringNotLike": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "StringLikeIgnoreCase": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Bool": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericNotEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericLessThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThan": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NumericGreaterThanEquals": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateBefore": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "DateAfter": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "IpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "NotIpAddress": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContains": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "ArrayContainsAll": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       },
       "Exists": {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "anyOf": [
          {
           "type": "string"
          },
          {
           "type": "number"
          },
          {
           "type": "boolean",
           "const": false
          },
          {
           "type": "boolean",
           "const": true
          },
          {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ConditionValue"
           }
          }
         ]
        }
       }
      },
      "additionalProperties": false
     },
     "tagClaims": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "type": "string"
        }
       }
      ]
     },
     "sourceIdentityClaim": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string"
       }
      ]
     }
    },
    "required": [
     "tenantId",
     "trustId"
    ],
    "additionalProperties": false
   },
   "DeliveryStatus": {
    "type": "string",
    "enum": [
     "pending",
     "failed",
     "delivered"
    ]
   },
   "AuditArchiveResult": {
    "type": "object",
    "properties": {
     "archived": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "number"
      },
      "description": "Events archived per tenant in this run."
     },
     "batches": {
      "type": "number"
     },
     "failed": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "code": {
         "type": "string"
        },
        "message": {
         "type": "string"
        }
       },
       "required": [
        "tenantId",
        "code",
        "message"
       ],
       "additionalProperties": false
      },
      "description": "Tenants whose chain did not verify or whose sink failed; their cursor did not move."
     },
     "gaps": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "fromSequence": {
         "type": "number"
        },
        "toSequence": {
         "type": "number"
        }
       },
       "required": [
        "tenantId",
        "fromSequence",
        "toSequence"
       ],
       "additionalProperties": false
      },
      "description": "Sequences deleted before they were archived (pruned without an archive configured)."
     },
     "busy": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Tenants another run is archiving right now (it holds their lease); they were skipped."
     },
     "truncated": {
      "type": "boolean",
      "description": "The run stopped at `limit`; run it again to continue."
     }
    },
    "required": [
     "archived",
     "batches",
     "failed",
     "gaps",
     "busy",
     "truncated"
    ],
    "additionalProperties": false
   },
   "AuthenticatedPrincipal": {
    "type": "object",
    "properties": {
     "identity": {
      "$ref": "#/components/schemas/Identity"
     },
     "session": {
      "$ref": "#/components/schemas/Session"
     }
    },
    "required": [
     "identity",
     "session"
    ],
    "additionalProperties": false
   },
   "Session": {
    "type": "object",
    "properties": {
     "identityId": {
      "type": "string"
     },
     "tokenHash": {
      "type": "string"
     },
     "createdAt": {
      "type": "number"
     },
     "expiresAt": {
      "type": "number"
     },
     "lastSeenAt": {
      "type": "number"
     },
     "authenticatedAt": {
      "type": "number"
     },
     "mfa": {
      "type": "boolean"
     },
     "kind": {
      "type": "string",
      "enum": [
       "user",
       "role",
       "api-key",
       "session-token",
       "delegated"
      ],
      "description": "`user` (sign-in and impersonation sessions), `api-key` (service-account and agent keys), `role` (assumed roles,\nincluding web-identity sessions), `session-token` (temporary credentials minted from a user session or API key) or\n`delegated` (an agent acting on a person's behalf under a delegation; the identity is the person). Code that\nbranches on the kind must fail closed for kinds it does not know."
     },
     "agentId": {
      "type": "string",
      "description": "Delegated sessions: the agent acting for the session's identity, and the delegation that allows it."
     },
     "delegationId": {
      "type": "string"
     },
     "mfaAuthenticatedAt": {
      "type": "number",
      "description": "When a first-hand second factor (TOTP, passkey or an MFA step-up) was last verified for this session; exposed to\npolicies as principal.mfaTime. Absent on remembered-device, impersonation, API-key, web-identity and legacy rows."
     },
     "credentialAuthorityId": {
      "type": "string",
      "description": "The authority that issued an API key; session tokens minted from the key copy it and are bounded by its ceilings."
     },
     "sessionName": {
      "type": "string",
      "description": "Temporary credentials: a caller-chosen label (/^[\\w+=,.@-]{2,64}$/), exposed as principal.sessionName."
     },
     "sourceIdentity": {
      "type": "string",
      "description": "Role sessions: the verified or trust-permitted source identity, exposed as principal.sourceIdentity."
     },
     "sessionTags": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "string"
      },
      "description": "Role sessions: per-session tags admitted by the trust, exposed as principal.sessionTags.{key}."
     },
     "sourcePolicy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "format": {
      "type": "string",
      "enum": [
       "jwt"
      ],
      "description": "Temporary credentials issued as IAM-signed session JWTs; the row stores the hash of the JWT like any token."
     },
     "audience": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Session JWTs: the audiences the token was issued for."
     },
     "webIdentity": {
      "type": "object",
      "properties": {
       "providerId": {
        "type": "string"
       },
       "issuer": {
        "type": "string"
       },
       "subject": {
        "type": "string"
       }
      },
      "required": [
       "providerId",
       "issuer",
       "subject"
      ],
      "additionalProperties": false,
      "description": "Web-identity role sessions: the OIDC provider and the verified external subject behind the session."
     },
     "method": {
      "type": "string",
      "enum": [
       "password",
       "passwordless-email",
       "passwordless-sms",
       "passkey",
       "federated",
       "impersonation"
      ],
      "description": "The sign-in method that established a user session; exposed to policies as principal.authMethod."
     },
     "client": {
      "$ref": "#/components/schemas/SessionClientInfo"
     },
     "name": {
      "type": "string",
      "description": "API keys only: an administrator-facing label and purpose (`credentials.create`/`credentials.update`)."
     },
     "description": {
      "type": "string"
     },
     "impersonatorId": {
      "type": "string",
      "description": "The administrator acting through this session (`identities.impersonate`); such sessions end with the administrator's own."
     },
     "impersonatorSessionId": {
      "type": "string"
     },
     "trustedDeviceId": {
      "type": "string",
      "description": "Set when a remembered device satisfied the MFA requirement instead of a fresh second factor."
     },
     "originalIdentityId": {
      "type": "string"
     },
     "sourceTenantId": {
      "type": "string"
     },
     "roleId": {
      "type": "string"
     },
     "trustId": {
      "type": "string"
     },
     "sourceSessionId": {
      "type": "string"
     },
     "sourceAuthorityIds": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "policy": {
      "$ref": "#/components/schemas/PolicyDocument"
     },
     "previousSignIn": {
      "$ref": "#/components/schemas/SignInRecord"
     },
     "id": {
      "type": "string"
     },
     "tenantId": {
      "type": "string"
     },
     "uniqueKey": {
      "type": "string",
      "description": "Optional database-enforced natural key, unique within collection and tenant."
     }
    },
    "required": [
     "identityId",
     "tokenHash",
     "createdAt",
     "expiresAt",
     "lastSeenAt",
     "authenticatedAt",
     "mfa",
     "kind",
     "id",
     "tenantId"
    ],
    "additionalProperties": {}
   },
   "AuthorizationRequest": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     },
     "headers": {
      "anyOf": [
       {
        "type": "array",
        "items": {
         "type": "array",
         "prefixItems": [
          {
           "type": "string"
          },
          {
           "type": "string"
          }
         ]
        }
       },
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "type": "string"
        }
       },
       {
        "$ref": "#/components/schemas/Headers"
       }
      ]
     },
     "token": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "action",
     "resource"
    ],
    "additionalProperties": false
   },
   "BatchAuthorizationRequest": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "checks": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AuthorizationCheck"
      }
     },
     "headers": {
      "anyOf": [
       {
        "type": "array",
        "items": {
         "type": "array",
         "prefixItems": [
          {
           "type": "string"
          },
          {
           "type": "string"
          }
         ]
        }
       },
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "type": "string"
        }
       },
       {
        "$ref": "#/components/schemas/Headers"
       }
      ]
     },
     "token": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "checks"
    ],
    "additionalProperties": false
   },
   "AuthorizationCheck": {
    "type": "object",
    "properties": {
     "action": {
      "type": "string"
     },
     "resource": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "id": {
        "type": "string"
       }
      },
      "required": [
       "type",
       "id"
      ],
      "additionalProperties": false
     }
    },
    "required": [
     "action",
     "resource"
    ],
    "additionalProperties": false
   },
   "BatchResult": {
    "allOf": [
     {
      "$ref": "#/components/schemas/AuthorizationCheck"
     },
     {
      "type": "object",
      "properties": {
       "allowed": {
        "type": "boolean"
       },
       "reason": {
        "type": "string"
       }
      },
      "required": [
       "allowed",
       "reason"
      ],
      "additionalProperties": false
     }
    ]
   },
   "InvariantCheckResult": {
    "type": "object",
    "properties": {
     "checked": {
      "type": "number"
     },
     "broken": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "invariantId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "violations": {
         "type": "array",
         "items": {
          "type": "string"
         }
        }
       },
       "required": [
        "tenantId",
        "invariantId",
        "name",
        "violations"
       ],
       "additionalProperties": false
      },
      "description": "Invariants that newly failed (or gained violators) since the previous check, audited as `invariant:broken`."
     },
     "restored": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "invariantId": {
         "type": "string"
        },
        "name": {
         "type": "string"
        }
       },
       "required": [
        "tenantId",
        "invariantId",
        "name"
       ],
       "additionalProperties": false
      },
      "description": "Invariants that pass again, audited as `invariant:restored`."
     }
    },
    "required": [
     "checked",
     "broken",
     "restored"
    ],
    "additionalProperties": false
   },
   "CertificationAutoCloseResult": {
    "type": "object",
    "properties": {
     "closed": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "campaignId": {
         "type": "string"
        },
        "outcomes": {
         "type": "object",
         "properties": {
          "kept": {
           "type": "number"
          },
          "revoked": {
           "type": "number"
          },
          "already-removed": {
           "type": "number"
          },
          "revocation-failed": {
           "type": "number"
          }
         },
         "required": [
          "kept",
          "revoked",
          "already-removed",
          "revocation-failed"
         ],
         "additionalProperties": false
        }
       },
       "required": [
        "tenantId",
        "campaignId",
        "outcomes"
       ],
       "additionalProperties": false
      },
      "description": "Campaigns closed in this run, with how their items ended."
     },
     "skipped": {
      "type": "number",
      "description": "Open auto-closing campaigns that are not due yet."
     }
    },
    "required": [
     "closed",
     "skipped"
    ],
    "additionalProperties": false
   },
   "Request": {
    "type": "object",
    "properties": {
     "cache": {
      "$ref": "#/components/schemas/RequestCache"
     },
     "credentials": {
      "$ref": "#/components/schemas/RequestCredentials"
     },
     "destination": {
      "$ref": "#/components/schemas/RequestDestination"
     },
     "headers": {
      "$ref": "#/components/schemas/Headers"
     },
     "integrity": {
      "type": "string",
      "description": "The **`integrity`** read-only property of the Request interface contains the subresource integrity value of the request.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity)"
     },
     "keepalive": {
      "type": "boolean",
      "description": "The **`keepalive`** read-only property of the Request interface contains the request's `keepalive` setting (`true` or `false`), which indicates whether the browser will keep the associated request alive if the page that initiated it is unloaded before the request is complete.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/keepalive)"
     },
     "method": {
      "type": "string",
      "description": "The **`method`** read-only property of the `POST`, etc.) A String indicating the method of the request.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method)"
     },
     "mode": {
      "$ref": "#/components/schemas/RequestMode"
     },
     "redirect": {
      "$ref": "#/components/schemas/RequestRedirect"
     },
     "referrer": {
      "type": "string",
      "description": "The **`referrer`** read-only property of the Request.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/referrer)"
     },
     "referrerPolicy": {
      "$ref": "#/components/schemas/ReferrerPolicy"
     },
     "signal": {
      "$ref": "#/components/schemas/AbortSignal"
     },
     "url": {
      "type": "string",
      "description": "The **`url`** read-only property of the Request interface contains the URL of the request.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url)"
     },
     "body": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/ReadableStream"
       }
      ],
      "description": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body)"
     },
     "bodyUsed": {
      "type": "boolean",
      "description": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed)"
     }
    },
    "required": [
     "cache",
     "credentials",
     "destination",
     "headers",
     "integrity",
     "keepalive",
     "method",
     "mode",
     "redirect",
     "referrer",
     "referrerPolicy",
     "signal",
     "url",
     "body",
     "bodyUsed"
    ],
    "additionalProperties": false
   },
   "RequestCache": {
    "type": "string",
    "enum": [
     "default",
     "force-cache",
     "no-cache",
     "no-store",
     "only-if-cached",
     "reload"
    ]
   },
   "RequestCredentials": {
    "type": "string",
    "enum": [
     "include",
     "omit",
     "same-origin"
    ]
   },
   "RequestDestination": {
    "type": "string",
    "enum": [
     "",
     "object",
     "document",
     "audio",
     "audioworklet",
     "embed",
     "font",
     "frame",
     "iframe",
     "image",
     "manifest",
     "paintworklet",
     "report",
     "script",
     "sharedworker",
     "style",
     "track",
     "video",
     "worker",
     "xslt"
    ]
   },
   "RequestMode": {
    "type": "string",
    "enum": [
     "same-origin",
     "cors",
     "navigate",
     "no-cors"
    ]
   },
   "RequestRedirect": {
    "type": "string",
    "enum": [
     "manual",
     "error",
     "follow"
    ]
   },
   "ReferrerPolicy": {
    "type": "string",
    "enum": [
     "",
     "same-origin",
     "no-referrer",
     "no-referrer-when-downgrade",
     "origin",
     "origin-when-cross-origin",
     "strict-origin",
     "strict-origin-when-cross-origin",
     "unsafe-url"
    ]
   },
   "AbortSignal": {
    "type": "object",
    "properties": {
     "aborted": {
      "type": "boolean",
      "description": "The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (`true`) or not (`false`).\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)"
     },
     "onabort": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "description": "Function (not serializable)"
       }
      ],
      "description": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event)"
     },
     "reason": {
      "description": "The **`reason`** read-only property returns a JavaScript value that indicates the abort reason.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason)"
     }
    },
    "required": [
     "aborted",
     "onabort",
     "reason"
    ],
    "additionalProperties": false
   },
   "ReadableStream": {
    "type": "object",
    "properties": {
     "locked": {
      "type": "boolean",
      "description": "The **`locked`** read-only property of the ReadableStream interface returns whether or not the readable stream is locked to a reader.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked)"
     }
    },
    "required": [
     "locked"
    ],
    "additionalProperties": false
   },
   "Response": {
    "type": "object",
    "properties": {
     "headers": {
      "$ref": "#/components/schemas/Headers"
     },
     "ok": {
      "type": "boolean",
      "description": "The **`ok`** read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok)"
     },
     "redirected": {
      "type": "boolean",
      "description": "The **`redirected`** read-only property of the Response interface indicates whether or not the response is the result of a request you made which was redirected.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected)"
     },
     "status": {
      "type": "number",
      "description": "The **`status`** read-only property of the Response interface contains the HTTP status codes of the response.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status)"
     },
     "statusText": {
      "type": "string",
      "description": "The **`statusText`** read-only property of the Response interface contains the status message corresponding to the HTTP status code in Response.status.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText)"
     },
     "type": {
      "$ref": "#/components/schemas/ResponseType"
     },
     "url": {
      "type": "string",
      "description": "The **`url`** read-only property of the Response interface contains the URL of the response.\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url)"
     },
     "body": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/ReadableStream"
       }
      ],
      "description": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body)"
     },
     "bodyUsed": {
      "type": "boolean",
      "description": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed)"
     }
    },
    "required": [
     "headers",
     "ok",
     "redirected",
     "status",
     "statusText",
     "type",
     "url",
     "body",
     "bodyUsed"
    ],
    "additionalProperties": false
   },
   "ResponseType": {
    "type": "string",
    "enum": [
     "error",
     "opaque",
     "default",
     "cors",
     "basic",
     "opaqueredirect"
    ]
   },
   "AccessibleResourcesRequest": {
    "type": "object",
    "properties": {
     "tenantId": {
      "type": "string"
     },
     "action": {
      "type": "string"
     },
     "type": {
      "type": "string"
     },
     "limit": {
      "type": "number"
     },
     "offset": {
      "type": "number"
     },
     "headers": {
      "anyOf": [
       {
        "type": "array",
        "items": {
         "type": "array",
         "prefixItems": [
          {
           "type": "string"
          },
          {
           "type": "string"
          }
         ]
        }
       },
       {
        "type": "object",
        "properties": {},
        "additionalProperties": {
         "type": "string"
        }
       },
       {
        "$ref": "#/components/schemas/Headers"
       }
      ]
     },
     "token": {
      "type": "string"
     }
    },
    "required": [
     "tenantId",
     "action",
     "type"
    ],
    "additionalProperties": false
   },
   "IncomingMessage": {
    "type": "object",
    "properties": {
     "aborted": {
      "type": "boolean",
      "description": "The `message.aborted` property will be `true` if the request has\nbeen aborted."
     },
     "httpVersion": {
      "type": "string",
      "description": "In case of server request, the HTTP version sent by the client. In the case of\nclient response, the HTTP version of the connected-to server.\nProbably either `'1.1'` or `'1.0'`.\n\nAlso `message.httpVersionMajor` is the first integer and `message.httpVersionMinor` is the second."
     },
     "httpVersionMajor": {
      "type": "number"
     },
     "httpVersionMinor": {
      "type": "number"
     },
     "complete": {
      "type": "boolean",
      "description": "The `message.complete` property will be `true` if a complete HTTP message has\nbeen received and successfully parsed.\n\nThis property is particularly useful as a means of determining if a client or\nserver fully transmitted a message before a connection was terminated:\n\n```js\nconst req = http.request({\n  host: '127.0.0.1',\n  port: 8080,\n  method: 'POST',\n}, (res) => {\n  res.resume();\n  res.on('end', () => {\n    if (!res.complete)\n      console.error(\n        'The connection was terminated while the message was still being sent');\n  });\n});\n```"
     },
     "connection": {
      "$ref": "#/components/schemas/Socket"
     },
     "socket": {
      "$ref": "#/components/schemas/Socket"
     },
     "headers": {
      "$ref": "#/components/schemas/IncomingHttpHeaders"
     },
     "headersDistinct": {
      "$ref": "#/components/schemas/Dict"
     },
     "rawHeaders": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The raw request/response headers list exactly as they were received.\n\nThe keys and values are in the same list. It is _not_ a\nlist of tuples. So, the even-numbered offsets are key values, and the\nodd-numbered offsets are the associated values.\n\nHeader names are not lowercased, and duplicates are not merged.\n\n```js\n// Prints something like:\n//\n// [ 'user-agent',\n//   'this is invalid because there can be only one',\n//   'User-Agent',\n//   'curl/7.22.0',\n//   'Host',\n//   '127.0.0.1:8000',\n//   'ACCEPT',\n//   '*' ]\nconsole.log(request.rawHeaders);\n```"
     },
     "trailers": {
      "$ref": "#/components/schemas/Dict2"
     },
     "trailersDistinct": {
      "$ref": "#/components/schemas/Dict"
     },
     "rawTrailers": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "The raw request/response trailer keys and values exactly as they were\nreceived. Only populated at the `'end'` event."
     },
     "method": {
      "type": "string",
      "description": "**Only valid for request obtained from {@link Server}.**\n\nThe request method as a string. Read only. Examples: `'GET'`, `'DELETE'`."
     },
     "url": {
      "type": "string",
      "description": "**Only valid for request obtained from {@link Server}.**\n\nRequest URL string. This contains only the URL that is present in the actual\nHTTP request. Take the following request:\n\n```http\nGET /status?name=ryan HTTP/1.1\nAccept: text/plain\n```\n\nTo parse the URL into its parts:\n\n```js\nnew URL(`http://${process.env.HOST ?? 'localhost'}${request.url}`);\n```\n\nWhen `request.url` is `'/status?name=ryan'` and `process.env.HOST` is undefined:\n\n```console\n$ node\n> new URL(`http://${process.env.HOST ?? 'localhost'}${request.url}`);\nURL {\n  href: 'http://localhost/status?name=ryan',\n  origin: 'http://localhost',\n  protocol: 'http:',\n  username: '',\n  password: '',\n  host: 'localhost',\n  hostname: 'localhost',\n  port: '',\n  pathname: '/status',\n  search: '?name=ryan',\n  searchParams: URLSearchParams { 'name' => 'ryan' },\n  hash: ''\n}\n```\n\nEnsure that you set `process.env.HOST` to the server's host name, or consider replacing this part entirely. If using `req.headers.host`, ensure proper\nvalidation is used, as clients may specify a custom `Host` header."
     },
     "statusCode": {
      "type": "number",
      "description": "**Only valid for response obtained from {@link ClientRequest}.**\n\nThe 3-digit HTTP response status code. E.G. `404`."
     },
     "statusMessage": {
      "type": "string",
      "description": "**Only valid for response obtained from {@link ClientRequest}.**\n\nThe HTTP response status message (reason phrase). E.G. `OK` or `Internal Server Error`."
     },
     "readableAborted": {
      "type": "boolean",
      "description": "Returns whether the stream was destroyed or errored before emitting `'end'`."
     },
     "readable": {
      "type": "boolean",
      "description": "Is `true` if it is safe to call {@link read}, which means\nthe stream has not been destroyed or emitted `'error'` or `'end'`."
     },
     "readableDidRead": {
      "type": "boolean",
      "description": "Returns whether `'data'` has been emitted."
     },
     "readableEncoding": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string",
        "const": "ascii"
       },
       {
        "type": "string",
        "const": "utf8"
       },
       {
        "type": "string",
        "const": "utf-8"
       },
       {
        "type": "string",
        "const": "utf16le"
       },
       {
        "type": "string",
        "const": "utf-16le"
       },
       {
        "type": "string",
        "const": "ucs2"
       },
       {
        "type": "string",
        "const": "ucs-2"
       },
       {
        "type": "string",
        "const": "base64"
       },
       {
        "type": "string",
        "const": "base64url"
       },
       {
        "type": "string",
        "const": "latin1"
       },
       {
        "type": "string",
        "const": "binary"
       },
       {
        "type": "string",
        "const": "hex"
       }
      ],
      "description": "Getter for the property `encoding` of a given `Readable` stream. The `encoding` property can be set using the {@link setEncoding} method."
     },
     "readableEnded": {
      "type": "boolean",
      "description": "Becomes `true` when [`'end'`](https://nodejs.org/docs/latest-v22.x/api/stream.html#event-end) event is emitted."
     },
     "readableFlowing": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "boolean",
        "const": false
       },
       {
        "type": "boolean",
        "const": true
       }
      ],
      "description": "This property reflects the current state of a `Readable` stream as described\nin the [Three states](https://nodejs.org/docs/latest-v22.x/api/stream.html#three-states) section."
     },
     "readableHighWaterMark": {
      "type": "number",
      "description": "Returns the value of `highWaterMark` passed when creating this `Readable`."
     },
     "readableLength": {
      "type": "number",
      "description": "This property contains the number of bytes (or objects) in the queue\nready to be read. The value provides introspection data regarding\nthe status of the `highWaterMark`."
     },
     "readableObjectMode": {
      "type": "boolean",
      "description": "Getter for the property `objectMode` of a given `Readable` stream."
     },
     "destroyed": {
      "type": "boolean",
      "description": "Is `true` after `readable.destroy()` has been called."
     },
     "closed": {
      "type": "boolean",
      "description": "Is `true` after `'close'` has been emitted."
     },
     "errored": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/Error"
       }
      ],
      "description": "Returns error if the stream has been destroyed with an error."
     },
     "_construct": {
      "description": "Function (not serializable)"
     },
     "__@captureRejectionSymbol@8333": {
      "description": "Function (not serializable)"
     }
    },
    "required": [
     "aborted",
     "httpVersion",
     "httpVersionMajor",
     "httpVersionMinor",
     "complete",
     "connection",
     "socket",
     "headers",
     "headersDistinct",
     "rawHeaders",
     "trailers",
     "trailersDistinct",
     "rawTrailers",
     "readableAborted",
     "readable",
     "readableDidRead",
     "readableEncoding",
     "readableEnded",
     "readableFlowing",
     "readableHighWaterMark",
     "readableLength",
     "readableObjectMode",
     "destroyed",
     "closed",
     "errored"
    ],
    "additionalProperties": false
   },
   "Socket": {
    "type": "object",
    "properties": {
     "autoSelectFamilyAttemptedAddresses": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "This property is only present if the family autoselection algorithm is enabled in `socket.connect(options)`\nand it is an array of the addresses that have been attempted.\n\nEach address is a string in the form of `$IP:$PORT`.\nIf the connection was successful, then the last address is the one that the socket is currently connected to."
     },
     "bufferSize": {
      "type": "number",
      "description": "This property shows the number of characters buffered for writing. The buffer\nmay contain strings whose length after encoding is not yet known. So this number\nis only an approximation of the number of bytes in the buffer.\n\n`net.Socket` has the property that `socket.write()` always works. This is to\nhelp users get up and running quickly. The computer cannot always keep up\nwith the amount of data that is written to a socket. The network connection\nsimply might be too slow. Node.js will internally queue up the data written to a\nsocket and send it out over the wire when it is possible.\n\nThe consequence of this internal buffering is that memory may grow.\nUsers who experience large or growing `bufferSize` should attempt to\n\"throttle\" the data flows in their program with `socket.pause()` and `socket.resume()`."
     },
     "bytesRead": {
      "type": "number",
      "description": "The amount of received bytes."
     },
     "bytesWritten": {
      "type": "number",
      "description": "The amount of bytes sent."
     },
     "connecting": {
      "type": "boolean",
      "description": "If `true`, `socket.connect(options[, connectListener])` was\ncalled and has not yet finished. It will stay `true` until the socket becomes\nconnected, then it is set to `false` and the `'connect'` event is emitted. Note\nthat the `socket.connect(options[, connectListener])` callback is a listener for the `'connect'` event."
     },
     "pending": {
      "type": "boolean",
      "description": "This is `true` if the socket is not connected yet, either because `.connect()`has not yet been called or because it is still in the process of connecting\n(see `socket.connecting`)."
     },
     "destroyed": {
      "type": "boolean",
      "description": "See `writable.destroyed` for further details."
     },
     "localAddress": {
      "type": "string",
      "description": "The string representation of the local IP address the remote client is\nconnecting on. For example, in a server listening on `'0.0.0.0'`, if a client\nconnects on `'192.168.1.1'`, the value of `socket.localAddress` would be`'192.168.1.1'`."
     },
     "localPort": {
      "type": "number",
      "description": "The numeric representation of the local port. For example, `80` or `21`."
     },
     "localFamily": {
      "type": "string",
      "description": "The string representation of the local IP family. `'IPv4'` or `'IPv6'`."
     },
     "readyState": {
      "$ref": "#/components/schemas/SocketReadyState"
     },
     "remoteAddress": {
      "type": "string",
      "description": "The string representation of the remote IP address. For example,`'74.125.127.100'` or `'2001:4860:a005::68'`. Value may be `undefined` if\nthe socket is destroyed (for example, if the client disconnected)."
     },
     "remoteFamily": {
      "type": "string",
      "description": "The string representation of the remote IP family. `'IPv4'` or `'IPv6'`. Value may be `undefined` if\nthe socket is destroyed (for example, if the client disconnected)."
     },
     "remotePort": {
      "type": "number",
      "description": "The numeric representation of the remote port. For example, `80` or `21`. Value may be `undefined` if\nthe socket is destroyed (for example, if the client disconnected)."
     },
     "timeout": {
      "type": "number",
      "description": "The socket timeout in milliseconds as set by `socket.setTimeout()`.\nIt is `undefined` if a timeout has not been set."
     },
     "allowHalfOpen": {
      "type": "boolean",
      "description": "If `false` then the stream will automatically end the writable side when the\nreadable side ends. Set initially by the `allowHalfOpen` constructor option,\nwhich defaults to `true`.\n\nThis can be changed manually to change the half-open behavior of an existing\n`Duplex` stream instance, but must be changed before the `'end'` event is emitted."
     },
     "__@captureRejectionSymbol@8333": {
      "description": "Function (not serializable)"
     },
     "readableAborted": {
      "type": "boolean",
      "description": "Returns whether the stream was destroyed or errored before emitting `'end'`."
     },
     "readable": {
      "type": "boolean",
      "description": "Is `true` if it is safe to call {@link read}, which means\nthe stream has not been destroyed or emitted `'error'` or `'end'`."
     },
     "readableDidRead": {
      "type": "boolean",
      "description": "Returns whether `'data'` has been emitted."
     },
     "readableEncoding": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "string",
        "const": "ascii"
       },
       {
        "type": "string",
        "const": "utf8"
       },
       {
        "type": "string",
        "const": "utf-8"
       },
       {
        "type": "string",
        "const": "utf16le"
       },
       {
        "type": "string",
        "const": "utf-16le"
       },
       {
        "type": "string",
        "const": "ucs2"
       },
       {
        "type": "string",
        "const": "ucs-2"
       },
       {
        "type": "string",
        "const": "base64"
       },
       {
        "type": "string",
        "const": "base64url"
       },
       {
        "type": "string",
        "const": "latin1"
       },
       {
        "type": "string",
        "const": "binary"
       },
       {
        "type": "string",
        "const": "hex"
       }
      ],
      "description": "Getter for the property `encoding` of a given `Readable` stream. The `encoding` property can be set using the {@link setEncoding} method."
     },
     "readableEnded": {
      "type": "boolean",
      "description": "Becomes `true` when [`'end'`](https://nodejs.org/docs/latest-v22.x/api/stream.html#event-end) event is emitted."
     },
     "readableFlowing": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "boolean",
        "const": false
       },
       {
        "type": "boolean",
        "const": true
       }
      ],
      "description": "This property reflects the current state of a `Readable` stream as described\nin the [Three states](https://nodejs.org/docs/latest-v22.x/api/stream.html#three-states) section."
     },
     "readableHighWaterMark": {
      "type": "number",
      "description": "Returns the value of `highWaterMark` passed when creating this `Readable`."
     },
     "readableLength": {
      "type": "number",
      "description": "This property contains the number of bytes (or objects) in the queue\nready to be read. The value provides introspection data regarding\nthe status of the `highWaterMark`."
     },
     "readableObjectMode": {
      "type": "boolean",
      "description": "Getter for the property `objectMode` of a given `Readable` stream."
     },
     "closed": {
      "type": "boolean",
      "description": "Is `true` after `'close'` has been emitted."
     },
     "errored": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/Error"
       }
      ],
      "description": "Returns error if the stream has been destroyed with an error."
     },
     "_construct": {
      "description": "Function (not serializable)"
     },
     "writable": {
      "type": "boolean",
      "description": "Is `true` if it is safe to call `writable.write()`, which means\nthe stream has not been destroyed, errored, or ended."
     },
     "writableAborted": {
      "type": "boolean",
      "description": "Returns whether the stream was destroyed or errored before emitting `'finish'`."
     },
     "writableEnded": {
      "type": "boolean",
      "description": "Is `true` after `writable.end()` has been called. This property\ndoes not indicate whether the data has been flushed, for this use `writable.writableFinished` instead."
     },
     "writableFinished": {
      "type": "boolean",
      "description": "Is set to `true` immediately before the `'finish'` event is emitted."
     },
     "writableHighWaterMark": {
      "type": "number",
      "description": "Return the value of `highWaterMark` passed when creating this `Writable`."
     },
     "writableLength": {
      "type": "number",
      "description": "This property contains the number of bytes (or objects) in the queue\nready to be written. The value provides introspection data regarding\nthe status of the `highWaterMark`."
     },
     "writableObjectMode": {
      "type": "boolean",
      "description": "Getter for the property `objectMode` of a given `Writable` stream."
     },
     "writableCorked": {
      "type": "number",
      "description": "Number of times `writable.uncork()` needs to be\ncalled in order to fully uncork the stream."
     },
     "writableNeedDrain": {
      "type": "boolean",
      "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`."
     },
     "_writev": {
      "description": "Function (not serializable)"
     }
    },
    "required": [
     "autoSelectFamilyAttemptedAddresses",
     "bufferSize",
     "bytesRead",
     "bytesWritten",
     "connecting",
     "pending",
     "destroyed",
     "readyState",
     "remoteAddress",
     "remoteFamily",
     "remotePort",
     "allowHalfOpen",
     "readableAborted",
     "readable",
     "readableDidRead",
     "readableEncoding",
     "readableEnded",
     "readableFlowing",
     "readableHighWaterMark",
     "readableLength",
     "readableObjectMode",
     "closed",
     "errored",
     "writable",
     "writableAborted",
     "writableEnded",
     "writableFinished",
     "writableHighWaterMark",
     "writableLength",
     "writableObjectMode",
     "writableCorked",
     "writableNeedDrain"
    ],
    "additionalProperties": false
   },
   "SocketReadyState": {
    "type": "string",
    "enum": [
     "open",
     "closed",
     "opening",
     "readOnly",
     "writeOnly"
    ]
   },
   "Error": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "message": {
      "type": "string"
     },
     "stack": {
      "type": "string"
     },
     "cause": {}
    },
    "required": [
     "name",
     "message"
    ],
    "additionalProperties": false
   },
   "IncomingHttpHeaders": {
    "type": "object",
    "properties": {
     "accept": {
      "type": "string"
     },
     "accept-encoding": {
      "type": "string"
     },
     "accept-language": {
      "type": "string"
     },
     "accept-patch": {
      "type": "string"
     },
     "accept-ranges": {
      "type": "string"
     },
     "access-control-allow-credentials": {
      "type": "string"
     },
     "access-control-allow-headers": {
      "type": "string"
     },
     "access-control-allow-methods": {
      "type": "string"
     },
     "access-control-allow-origin": {
      "type": "string"
     },
     "access-control-expose-headers": {
      "type": "string"
     },
     "access-control-max-age": {
      "type": "string"
     },
     "access-control-request-headers": {
      "type": "string"
     },
     "access-control-request-method": {
      "type": "string"
     },
     "age": {
      "type": "string"
     },
     "allow": {
      "type": "string"
     },
     "alt-svc": {
      "type": "string"
     },
     "authorization": {
      "type": "string"
     },
     "cache-control": {
      "type": "string"
     },
     "connection": {
      "type": "string"
     },
     "content-disposition": {
      "type": "string"
     },
     "content-encoding": {
      "type": "string"
     },
     "content-language": {
      "type": "string"
     },
     "content-length": {
      "type": "string"
     },
     "content-location": {
      "type": "string"
     },
     "content-range": {
      "type": "string"
     },
     "content-type": {
      "type": "string"
     },
     "cookie": {
      "type": "string"
     },
     "date": {
      "type": "string"
     },
     "etag": {
      "type": "string"
     },
     "expect": {
      "type": "string"
     },
     "expires": {
      "type": "string"
     },
     "forwarded": {
      "type": "string"
     },
     "from": {
      "type": "string"
     },
     "host": {
      "type": "string"
     },
     "if-match": {
      "type": "string"
     },
     "if-modified-since": {
      "type": "string"
     },
     "if-none-match": {
      "type": "string"
     },
     "if-unmodified-since": {
      "type": "string"
     },
     "last-modified": {
      "type": "string"
     },
     "location": {
      "type": "string"
     },
     "origin": {
      "type": "string"
     },
     "pragma": {
      "type": "string"
     },
     "proxy-authenticate": {
      "type": "string"
     },
     "proxy-authorization": {
      "type": "string"
     },
     "public-key-pins": {
      "type": "string"
     },
     "range": {
      "type": "string"
     },
     "referer": {
      "type": "string"
     },
     "retry-after": {
      "type": "string"
     },
     "sec-fetch-site": {
      "type": "string"
     },
     "sec-fetch-mode": {
      "type": "string"
     },
     "sec-fetch-user": {
      "type": "string"
     },
     "sec-fetch-dest": {
      "type": "string"
     },
     "sec-websocket-accept": {
      "type": "string"
     },
     "sec-websocket-extensions": {
      "type": "string"
     },
     "sec-websocket-key": {
      "type": "string"
     },
     "sec-websocket-protocol": {
      "type": "string"
     },
     "sec-websocket-version": {
      "type": "string"
     },
     "set-cookie": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "strict-transport-security": {
      "type": "string"
     },
     "tk": {
      "type": "string"
     },
     "trailer": {
      "type": "string"
     },
     "transfer-encoding": {
      "type": "string"
     },
     "upgrade": {
      "type": "string"
     },
     "user-agent": {
      "type": "string"
     },
     "vary": {
      "type": "string"
     },
     "via": {
      "type": "string"
     },
     "warning": {
      "type": "string"
     },
     "www-authenticate": {
      "type": "string"
     }
    },
    "additionalProperties": {
     "anyOf": [
      {
       "type": "string"
      },
      {
       "type": "array",
       "items": {
        "type": "string"
       }
      }
     ]
    }
   },
   "Dict": {
    "type": "object",
    "properties": {},
    "additionalProperties": {
     "type": "array",
     "items": {
      "type": "string"
     }
    }
   },
   "Dict2": {
    "type": "object",
    "properties": {},
    "additionalProperties": {
     "type": "string"
    }
   },
   "ServerResponse": {
    "type": "object",
    "properties": {
     "statusCode": {
      "type": "number",
      "description": "When using implicit headers (not calling `response.writeHead()` explicitly),\nthis property controls the status code that will be sent to the client when\nthe headers get flushed.\n\n```js\nresponse.statusCode = 404;\n```\n\nAfter response header was sent to the client, this property indicates the\nstatus code which was sent out."
     },
     "statusMessage": {
      "type": "string",
      "description": "When using implicit headers (not calling `response.writeHead()` explicitly),\nthis property controls the status message that will be sent to the client when\nthe headers get flushed. If this is left as `undefined` then the standard\nmessage for the status code will be used.\n\n```js\nresponse.statusMessage = 'Not found';\n```\n\nAfter response header was sent to the client, this property indicates the\nstatus message which was sent out."
     },
     "strictContentLength": {
      "type": "boolean",
      "description": "If set to `true`, Node.js will check whether the `Content-Length` header value and the size of the body, in bytes, are equal.\nMismatching the `Content-Length` header value will result\nin an `Error` being thrown, identified by `code:``'ERR_HTTP_CONTENT_LENGTH_MISMATCH'`."
     },
     "req": {
      "$ref": "#/components/schemas/IncomingMessage"
     },
     "chunkedEncoding": {
      "type": "boolean"
     },
     "shouldKeepAlive": {
      "type": "boolean"
     },
     "useChunkedEncodingByDefault": {
      "type": "boolean"
     },
     "sendDate": {
      "type": "boolean"
     },
     "finished": {
      "type": "boolean"
     },
     "headersSent": {
      "type": "boolean",
      "description": "Read-only. `true` if the headers were sent, otherwise `false`."
     },
     "connection": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/Socket"
       }
      ],
      "description": "Alias of `outgoingMessage.socket`."
     },
     "socket": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/Socket"
       }
      ],
      "description": "Reference to the underlying socket. Usually, users will not want to access\nthis property.\n\nAfter calling `outgoingMessage.end()`, this property will be nulled."
     },
     "writable": {
      "type": "boolean",
      "description": "Is `true` if it is safe to call `writable.write()`, which means\nthe stream has not been destroyed, errored, or ended."
     },
     "writableAborted": {
      "type": "boolean",
      "description": "Returns whether the stream was destroyed or errored before emitting `'finish'`."
     },
     "writableEnded": {
      "type": "boolean",
      "description": "Is `true` after `writable.end()` has been called. This property\ndoes not indicate whether the data has been flushed, for this use `writable.writableFinished` instead."
     },
     "writableFinished": {
      "type": "boolean",
      "description": "Is set to `true` immediately before the `'finish'` event is emitted."
     },
     "writableHighWaterMark": {
      "type": "number",
      "description": "Return the value of `highWaterMark` passed when creating this `Writable`."
     },
     "writableLength": {
      "type": "number",
      "description": "This property contains the number of bytes (or objects) in the queue\nready to be written. The value provides introspection data regarding\nthe status of the `highWaterMark`."
     },
     "writableObjectMode": {
      "type": "boolean",
      "description": "Getter for the property `objectMode` of a given `Writable` stream."
     },
     "writableCorked": {
      "type": "number",
      "description": "Number of times `writable.uncork()` needs to be\ncalled in order to fully uncork the stream."
     },
     "destroyed": {
      "type": "boolean",
      "description": "Is `true` after `writable.destroy()` has been called."
     },
     "closed": {
      "type": "boolean",
      "description": "Is `true` after `'close'` has been emitted."
     },
     "errored": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/Error"
       }
      ],
      "description": "Returns error if the stream has been destroyed with an error."
     },
     "writableNeedDrain": {
      "type": "boolean",
      "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`."
     },
     "_writev": {
      "description": "Function (not serializable)"
     },
     "_construct": {
      "description": "Function (not serializable)"
     },
     "__@captureRejectionSymbol@8333": {
      "description": "Function (not serializable)"
     }
    },
    "required": [
     "statusCode",
     "statusMessage",
     "strictContentLength",
     "req",
     "chunkedEncoding",
     "shouldKeepAlive",
     "useChunkedEncodingByDefault",
     "sendDate",
     "finished",
     "headersSent",
     "connection",
     "socket",
     "writable",
     "writableAborted",
     "writableEnded",
     "writableFinished",
     "writableHighWaterMark",
     "writableLength",
     "writableObjectMode",
     "writableCorked",
     "destroyed",
     "closed",
     "errored",
     "writableNeedDrain"
    ],
    "additionalProperties": false
   },
   "PurgeResult": {
    "type": "object",
    "properties": {
     "purgedTenants": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "deletedRecords": {
      "type": "number"
     },
     "expiredBindings": {
      "type": "number"
     },
     "expiredRequests": {
      "type": "number"
     },
     "expiredIdentities": {
      "type": "number",
      "description": "Identities past their scheduled deactivation that the worker disabled in this run."
     },
     "expiredActivations": {
      "type": "number",
      "description": "Ended role activations removed in this run."
     },
     "expiredMemberships": {
      "type": "number",
      "description": "Temporary group memberships past their end removed in this run."
     },
     "expiredAssignments": {
      "type": "number",
      "description": "Access-package assignments past their end removed in this run (their bindings and memberships expire on their own)."
     }
    },
    "required": [
     "purgedTenants",
     "deletedRecords",
     "expiredBindings",
     "expiredRequests",
     "expiredIdentities",
     "expiredActivations",
     "expiredMemberships",
     "expiredAssignments"
    ],
    "additionalProperties": false
   },
   "SecretRotationOptions": {
    "type": "object",
    "properties": {
     "dryRun": {
      "type": "boolean",
      "description": "Report what would change without writing anything."
     },
     "batchSize": {
      "type": "number",
      "description": "Records read per transaction (default 200, 1-2000)."
     },
     "limit": {
      "type": "number",
      "description": "Most records examined per collection (a sample, for `selfCheck`); by default all of them."
     }
    },
    "additionalProperties": false
   },
   "SecretRotationResult": {
    "type": "object",
    "properties": {
     "resealed": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "number"
      },
      "description": "Values re-sealed with the current secret (or, in a dry run, that would be), per collection."
     },
     "unreadable": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "number"
      },
      "description": "Values no configured secret opens, per collection: sealed with a secret that is gone."
     },
     "current": {
      "type": "number",
      "description": "Values already sealed with the current secret."
     },
     "complete": {
      "type": "boolean",
      "description": "Every record was examined; false when `limit` cut a collection short (the counts are a sample)."
     },
     "done": {
      "type": "boolean",
      "description": "Nothing sealed with a previous secret remains: the run examined everything and (in a dry run)\nfound nothing to re-seal. Only then can `previousSecrets` go."
     }
    },
    "required": [
     "resealed",
     "unreadable",
     "current",
     "complete",
     "done"
    ],
    "additionalProperties": false
   },
   "SelfCheckOptions": {
    "type": "object",
    "properties": {
     "cap": {
      "type": "number",
      "description": "Most records counted per backlog check (default 1000); larger backlogs report the cap."
     },
     "deliveryRetentionMs": {
      "type": "number",
      "description": "The `deliveryRetentionMs` your sweep runs with (default 30 days), so its backlog is judged the same way."
     },
     "graceMs": {
      "type": "number",
      "description": "The `graceMs` your sweep runs with (default 5 minutes)."
     }
    },
    "additionalProperties": false
   },
   "SelfCheckResult": {
    "type": "object",
    "properties": {
     "ok": {
      "type": "boolean",
      "description": "No error findings."
     },
     "findings": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/SelfCheckFinding"
      }
     },
     "storage": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "$ref": "#/components/schemas/StoreDescription"
       }
      ],
      "description": "The adapter's own view, when it can describe itself."
     }
    },
    "required": [
     "ok",
     "findings",
     "storage"
    ],
    "additionalProperties": false
   },
   "SelfCheckFinding": {
    "type": "object",
    "properties": {
     "check": {
      "type": "string",
      "description": "Stable identifier, for suppressing or alerting on one check."
     },
     "severity": {
      "$ref": "#/components/schemas/SelfCheckSeverity"
     },
     "message": {
      "type": "string"
     },
     "fix": {
      "type": "string"
     },
     "count": {
      "type": "number",
      "description": "How many records the finding concerns, when it is about stored data (capped at `cap`)."
     }
    },
    "required": [
     "check",
     "severity",
     "message",
     "fix"
    ],
    "additionalProperties": false
   },
   "SelfCheckSeverity": {
    "type": "string",
    "enum": [
     "warning",
     "info",
     "error"
    ]
   },
   "StoreDescription": {
    "type": "object",
    "properties": {
     "adapter": {
      "type": "string"
     },
     "schemaVersion": {
      "anyOf": [
       {
        "type": "null"
       },
       {
        "type": "number"
       }
      ]
     },
     "migrations": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "appliedAt": {
         "type": "number"
        }
       },
       "required": [
        "name",
        "appliedAt"
       ],
       "additionalProperties": false
      }
     },
     "collections": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "records": {
         "type": "number"
        }
       },
       "required": [
        "name",
        "records"
       ],
       "additionalProperties": false
      }
     },
     "settings": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "anyOf": [
        {
         "type": "null"
        },
        {
         "type": "string"
        },
        {
         "type": "number"
        },
        {
         "type": "boolean",
         "const": false
        },
        {
         "type": "boolean",
         "const": true
        }
       ]
      },
      "description": "Adapter settings worth checking in production (journal mode, durability, size, server version)."
     }
    },
    "required": [
     "adapter",
     "schemaVersion",
     "migrations",
     "collections",
     "settings"
    ],
    "additionalProperties": false
   },
   "AccessDigestResult": {
    "type": "object",
    "properties": {
     "sent": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "recipients": {
         "type": "number"
        },
        "expiringIdentities": {
         "type": "number"
        },
        "expiringBindings": {
         "type": "number"
        },
        "startingBindings": {
         "type": "number"
        },
        "expiringMemberships": {
         "type": "number"
        },
        "activations": {
         "type": "number"
        },
        "pendingRequests": {
         "type": "number"
        },
        "unusedKeys": {
         "type": "number"
        },
        "expiringKeys": {
         "type": "number"
        }
       },
       "required": [
        "tenantId",
        "recipients",
        "expiringIdentities",
        "expiringBindings",
        "startingBindings",
        "expiringMemberships",
        "activations",
        "pendingRequests",
        "unusedKeys",
        "expiringKeys"
       ],
       "additionalProperties": false
      },
      "description": "Tenants whose owners were emailed, with the recipient count and the finding counts."
     },
     "skipped": {
      "type": "object",
      "properties": {
       "inactive": {
        "type": "number"
       },
       "recent": {
        "type": "number"
       },
       "quiet": {
        "type": "number"
       },
       "noOwners": {
        "type": "number"
       }
      },
      "required": [
       "inactive",
       "recent",
       "quiet",
       "noOwners"
      ],
      "additionalProperties": false,
      "description": "Tenants left alone: not active, digested within the interval, nothing to report, or no owner with an email."
     }
    },
    "required": [
     "sent",
     "skipped"
    ],
    "additionalProperties": false
   },
   "ExpiryReminderResult": {
    "type": "object",
    "properties": {
     "sent": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "tenantId": {
         "type": "string"
        },
        "identityId": {
         "type": "string"
        },
        "items": {
         "type": "number"
        }
       },
       "required": [
        "tenantId",
        "identityId",
        "items"
       ],
       "additionalProperties": false
      },
      "description": "One entry per person emailed, with how many of their items end within the window."
     },
     "skipped": {
      "type": "object",
      "properties": {
       "inactive": {
        "type": "number"
       },
       "quiet": {
        "type": "number"
       }
      },
      "required": [
       "inactive",
       "quiet"
      ],
      "additionalProperties": false,
      "description": "Tenants left alone: not active, or with nothing new to remind anyone about."
     }
    },
    "required": [
     "sent",
     "skipped"
    ],
    "additionalProperties": false
   },
   "SweepOptions": {
    "type": "object",
    "properties": {
     "now": {
      "type": "number",
      "description": "Epoch milliseconds to treat as now, for tests and backfills. By default each collection is\njudged by the clock its writer uses: the authentication clock for sessions, devices,\nrelationships, the outbox, and audit hooks, and the wall clock for OAuth, SAML, and SSF records."
     },
     "batchSize": {
      "type": "number",
      "description": "Records read per transaction (default 500, 1-5000). Short transactions keep the write lock brief."
     },
     "limit": {
      "type": "number",
      "description": "The most records one run deletes (default 10000, at most 1000000); see `truncated`."
     },
     "deliveryRetentionMs": {
      "type": "number",
      "description": "How long delivered and abandoned deliveries stay (outbox email, SMS, and webhook history, failed\nShared Signals deliveries): default 30 days, 0-3650 days. Pending deliveries are never swept."
     },
     "graceMs": {
      "type": "number",
      "description": "A margin past expiry before a record is deleted, against clock skew between instances (default 5 minutes, at most 1 day)."
     }
    },
    "additionalProperties": false
   },
   "SweepResult": {
    "type": "object",
    "properties": {
     "deleted": {
      "type": "object",
      "properties": {},
      "additionalProperties": {
       "type": "number"
      },
      "description": "Records deleted per collection; collections with nothing to delete are omitted."
     },
     "total": {
      "type": "number"
     },
     "truncated": {
      "type": "boolean",
      "description": "The run stopped at `limit` before finishing; more records may be due, so run it again."
     }
    },
    "required": [
     "deleted",
     "total",
     "truncated"
    ],
    "additionalProperties": false
   },
   "ProtocolService": {
    "type": "object",
    "properties": {
     "basePath": {
      "type": "string"
     },
     "handler": {
      "description": "Function (not serializable)"
     },
     "nodeHandler": {
      "description": "Function (not serializable)"
     }
    },
    "required": [
     "basePath"
    ],
    "additionalProperties": false
   },
   "ErrorEnvelope": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "object",
      "required": [
       "code",
       "message"
      ],
      "properties": {
       "code": {
        "type": "string",
        "description": "Stable error code, for example ACCESS_DENIED."
       },
       "message": {
        "type": "string",
        "description": "Human-readable explanation; may change between releases."
       },
       "retryAfterMs": {
        "type": "number",
        "description": "Present on RATE_LIMITED."
       }
      }
     }
    }
   }
  }
 }
}