openapi: 3.1.0
info:
  title: Silicon IAM API
  version: 1.0.0
  summary: Secure identity, organization governance, application login, and delegated access.
  description: |
    Normative public and privileged HTTP contract for Silicon IAM. PostgreSQL is
    authoritative; webhook delivery never substitutes for online authorization.
    Timestamps are UTC RFC 3339 values. One-time secret responses can be replayed
    for ten minutes only by the same caller using the same route, request digest,
    and Idempotency-Key.
    IAM-managed six-digit OTPs expire after ten minutes. Signup, Carbon login,
    invitation-join, and verified-channel step-up challenges allow ten failed
    verifications, then enter a 60-second cooldown before the current unexpired
    challenge receives a fresh ten-attempt window. Replacement codes inherit
    the partial failed-attempt count or active cooldown.
  contact: { email: auth@teamofsilicons.com }
servers:
  - { url: 'https://backend.iam.teamofsilicons.com', description: Production }
tags:
  - { name: System }
  - { name: Carbon signup }
  - { name: Carbon authentication }
  - { name: Carbon account }
  - { name: OAuth }
  - { name: Organizations }
  - { name: Memberships }
  - { name: Invitations }
  - { name: Silicons }
  - { name: Tags }
  - { name: Trust }
  - { name: Governance }
  - { name: SSO }
  - name: Applications
    description: >-
      Applications are organization-owned. Non-platform Application management
      requires a direct Carbon IAM bearer and current active owner/admin
      membership in the Application's organization.
  - name: OBO Access
    description: >-
      Strictly same-organization Application discovery, request-bound proof
      exchange, and single-use audience verification.
  - { name: Webhooks }
  - name: Testing environments
    description: >-
      A testing environment is an organization-owned replica of Silicon IAM
      running against a separate database and starting completely empty. Every
      plane-selectable route accepts an `X-Testing-Environment-Key` header; supplying one
      executes that request inside the named environment instead of production,
      against the same contract. OTP delivery is suppressed and verification
      accepts `000000`. Test webhook deliveries are explicitly marked and
      carry their environment key. Organization-prefixed lifecycle routes in
      this section always operate on production; the singular test-only routes
      require the environment header.
  - { name: Platform administration }

paths:
  /healthz:
    get:
      tags: [System]
      operationId: getLiveness
      security: []
      responses:
        '200': { $ref: '#/components/responses/HealthOk' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /readyz:
    get:
      tags: [System]
      operationId: getReadiness
      description: >-
        Verifies the production database schema and, when configured, the
        shared testing database's base plus testing-overlay migration ledgers.
      security: []
      responses:
        '200': { $ref: '#/components/responses/HealthOk' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/version:
    get:
      tags: [System]
      operationId: negotiateApiVersion
      summary: Negotiate a public API version before using any versioned route.
      description: >-
        The client advertises every API major it implements in descending
        preference order. IAM selects the highest common version. Official
        clients fail closed when no common version exists or the response does
        not match the advertised intersection.
      security: []
      parameters:
        - name: Silicon-IAM-Supported-API-Versions
          in: header
          required: true
          description: Comma-separated distinct API majors in descending client preference order.
          schema:
            type: string
            minLength: 2
            maxLength: 255
            pattern: '^v[1-9][0-9]{0,8}(, *v[1-9][0-9]{0,8}){0,15}$'
      responses:
        '200':
          description: The client and server agreed on the highest common API version.
          headers:
            Silicon-IAM-API-Version:
              required: true
              schema: { type: string, pattern: '^v[1-9][0-9]{0,8}$' }
            Vary:
              required: true
              schema: { const: Silicon-IAM-Supported-API-Versions }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiVersionNegotiation' }
        '406': { $ref: '#/components/responses/NotAcceptable' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/version:
    get:
      tags: [System]
      operationId: getVersion
      security: []
      responses:
        '200':
          description: Running contract and build version.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VersionInfo' }
  /api/v1/signup/sessions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: createSignupSession
      security: []
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      responses:
        '201':
          description: Signup session created for 48 hours.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthSession' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/signup/sessions/{session_id}/email:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: sendSignupEmailCode
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EmailInput' }
      responses:
        '202': { $ref: '#/components/responses/CodeDispatchAccepted' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/signup/sessions/{session_id}/email/verify:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: verifySignupEmail
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody: { $ref: '#/components/requestBodies/VerificationCode' }
      responses:
        '200': { $ref: '#/components/responses/Verified' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/signup/sessions/{session_id}/phone:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: sendSignupPhoneCode
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PhoneInput' }
      responses:
        '202': { $ref: '#/components/responses/CodeDispatchAccepted' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/signup/sessions/{session_id}/phone/verify:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: verifySignupPhone
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody: { $ref: '#/components/requestBodies/VerificationCode' }
      responses:
        '200': { $ref: '#/components/responses/Verified' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/signup/sessions/{session_id}/complete:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon signup]
      operationId: completeCarbonSignup
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CarbonSignupComplete' }
      responses:
        '201':
          description: Carbon created after verified identities are rechecked atomically.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CarbonSelf' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/carbon-ids/{carbon_id}/availability:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Carbon signup]
      operationId: checkCarbonIdAvailability
      security: []
      parameters: [{ $ref: '#/components/parameters/CarbonIdPath' }]
      responses:
        '200':
          description: Current availability; this does not reserve the handle.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Availability' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/carbons/search:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Carbon account]
      operationId: searchCarbons
      security: [{ iamBearer: [] }]
      parameters:
        - { name: q, in: query, required: true, schema: { type: string, minLength: 1, maxLength: 100 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 10, default: 10 } }
      responses:
        '200':
          description: Zero to ten fuzzy Carbon ID suggestions; no profile or contact data is returned.
          content:
            application/json:
              schema:
                type: object
                required: [items]
                properties:
                  items: { type: array, maxItems: 10, items: { $ref: '#/components/schemas/CarbonSuggestion' } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/carbons/resolve/email:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon account]
      operationId: resolveCarbonByEmail
      description: Resolve an exact active verified email to its Carbon ID. Only an authenticated direct Carbon may call this endpoint.
      security: [{ iamBearer: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EmailInput' }
      responses:
        '200':
          description: Exact active Carbon identity resolved.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CarbonResolution' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/carbons/resolve/phone:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon account]
      operationId: resolveCarbonByPhone
      description: Resolve an exact active verified phone number to its Carbon ID. Only an authenticated direct Carbon may call this endpoint.
      security: [{ iamBearer: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PhoneInput' }
      responses:
        '200':
          description: Exact active Carbon identity resolved.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CarbonResolution' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/login:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Login]
      operationId: startLogin
      summary: Open IAM sign-in and user-controlled organization selection.
      description: |
        Navigates to IAM sign-in, Application validation and explicit user organization
        selection. GET never changes consent or issues a token. Apps must not supply
        org_id. IAM requires at least one selected active organization; the trusted
        IAM frontend submits the choice by POST. After authorization, the frontend
        redirects with a two-minute single-use SLT, or displays it when no callback
        was supplied. Selections are additive on the same parent IAM session.
      security: [{ sessionCookie: [] }]
      parameters:
        - { name: app_ids, in: query, schema: { type: string }, description: Comma-separated canonical app IDs for batch login; 1–100 unique apps. Mutually exclusive with app_id. Batch callbacks use a JSON slts array in the URL fragment. }
        - { name: app_id, in: query, schema: { $ref: '#/components/schemas/AppId' } }
        - { name: redirect_uri, in: query, schema: { type: string, format: uri, maxLength: 2048 } }
      responses:
        '302':
          description: The IAM authentication and organization-consent frontend.
          headers: { Location: { schema: { type: string, format: uri } } }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/login/status:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Login]
      operationId: readLoginStatus
      summary: Report what became of a short-lived token that was shown.
      description: |
        After displaying an SLT without a callback, the IAM frontend navigates
        here at token expiry. The request must belong to the current IAM session.
        The result reports whether the token was exchanged or expired.
      security: [{ sessionCookie: [] }]
      parameters:
        - { name: request, in: query, required: true, schema: { type: string, format: uuid } }
      responses:
        '200':
          description: Whether the token was exchanged or expired, as HTML.
          content: { text/html: { schema: { type: string } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/v1/app-auth/tokens:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Login]
      operationId: exchangeApplicationToken
      summary: Trade a short-lived token, or a refresh token, for a session.
      description: |
        The application authenticates itself the same way in both cases, so
        which one it is asking for is simply which credential it presented.
        Present exactly one of `slt` and `refresh_token`.
      security: [{ applicationBasic: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema: { $ref: '#/components/schemas/ApplicationTokenRequest' }
      responses:
        '200':
          description: Opaque access token and rotating refresh token.
          headers:
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OAuthTokenResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/app-auth/organizations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Login]
      operationId: listLoginOrganizations
      summary: Validate an application and list organization choices for the IAM user.
      description: Direct IAM Carbon/Silicon bearer only. Application credentials cannot list unselected organizations. Previously selected organizations on this parent IAM session are marked authorized.
      security: [{ bearerAuth: [] }]
      parameters:
        - { name: app_id, in: query, required: true, schema: { $ref: '#/components/schemas/AppId' } }
      responses:
        '200':
          description: Verified application and active organization choices.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LoginOrganizations' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/v1/app-auth/batch/organizations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Login]
      operationId: listBatchLoginOrganizations
      summary: Validate 1–100 apps and list the IAM user's organization choices for each.
      description: Direct IAM Carbon/Silicon session only. Rejects unknown, unverified, duplicate or malformed apps as a whole. Does not grant consent or mint tokens.
      security: [{ bearerAuth: [] }]
      parameters:
        - { name: app_ids, in: query, required: true, schema: { type: string }, description: Comma-separated canonical app IDs; 1–100 unique apps. }
      responses:
        '200':
          description: Verified apps in requested order, each with organization choices.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BatchLoginOrganizations' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/app-auth/batch/short-lived-tokens:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Login]
      operationId: issueBatchShortLivedTokens
      summary: Atomically authorize 1–100 applications from one direct IAM session.
      description: Each app receives independent additive organization consent and a two-minute single-use SLT. Every selection must be explicit and active. Any failure rolls back the whole batch. Replay is bound to the exact request and parent IAM session; it never extends token expiry. Applications exchange only their own SLT with their own secret at the existing token endpoint.
      security: [{ bearerAuth: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BatchLoginRequest' }
      responses:
        '201':
          description: Separate SLTs in the requested application order.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BatchLoginTokens' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/app-auth/short-lived-tokens:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Login]
      operationId: issueShortLivedToken
      summary: Get a short-lived token while already signed in.
      description: |
        For a direct IAM caller that explicitly selects org_ids -- a Silicon, which has no
        browser to be redirected in, or a Carbon that should not have to start
        another login. The token is valid for two minutes and for a single
        exchange, and the application completes it at /api/v1/app-auth/tokens
        exactly as it would one delivered through a redirect.
      security: [{ bearerAuth: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ShortLivedTokenRequest' }
      responses:
        '201':
          description: The short-lived token and its lifetime in seconds.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ShortLivedToken' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/oauth/introspect:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [OAuth]
      operationId: introspectOAuthToken
      security: [{ applicationBasic: [] }]
      parameters: [{ $ref: '#/components/parameters/OrgContext' }]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema: { $ref: '#/components/schemas/TokenIntrospectionRequest' }
      responses:
        '200':
          description: Activity after current revocation and membership checks.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TokenIntrospection' }
        '400':
          description: The token-type hint or optional X-Org-ID header is malformed or duplicated.
          content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/oauth/revoke:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [OAuth]
      operationId: revokeOAuthToken
      description: |
        Revoking an access token invalidates that token only. Revoking a refresh
        token invalidates its complete refresh family and access authority issued
        for the same Application session. Unknown tokens deliberately succeed.
      security: [{ applicationBasic: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema: { $ref: '#/components/schemas/OAuthRevocationRequest' }
      responses:
        '200':
          description: Revocation accepted; unknown tokens also return 200.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/login/challenges:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: createLoginChallenge
      security: []
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/LoginChallengeCreate' }
      responses:
        '201':
          description: Challenge created for an existing Carbon; Carbon-ID login may dispatch to both verified channels.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthSession' }
        '404':
          description: No active Carbon exists for the submitted email, phone number, or Carbon ID.
          content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/login/challenges/{session_id}/verify:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: verifyLoginChallenge
      security: []
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody: { $ref: '#/components/requestBodies/VerificationCode' }
      responses:
        '200':
          description: Carbon session established and every code in the challenge consumed.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/IamTokenResponse' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/step-up/challenges:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: createStepUpChallenge
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/StepUpChallengeCreate' }
      responses:
        '201':
          description: Reauthentication code sent to a verified channel.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthSession' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/step-up/challenges/{session_id}/verify:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: verifyStepUpChallenge
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody: { $ref: '#/components/requestBodies/VerificationCode' }
      responses:
        '200':
          description: Five-minute action-bound step-up token.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StepUpTokenResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/auth/tokens/refresh:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: refreshIamToken
      security: []
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RefreshTokenRequest' }
      responses:
        '200':
          description: Rotated tokens; reuse of a consumed refresh token revokes its family.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/IamTokenResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/logout:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication]
      operationId: logout
      description: >-
        Revokes the current session immediately by default. If all_sessions
        would revoke another active session, every target and the authenticating
        session must be at least 12 hours old and X-Step-Up-Token must carry a
        verified-channel account.sessions_revoke_all assertion bound to the
        current Carbon principal. Browser-cookie authentication additionally
        requires X-CSRF-Token to exactly match the token bound into the signed
        session cookie; bearer authentication does not. The operation fails
        atomically if any active target is younger. A Carbon OAuth bearer may
        also trigger logout only when its client is its audience; that form
        revokes every IAM and Application authority bound to the parent session
        across configured Applications and cannot select all_sessions.
      security: [{ iamBearer: [] }, { sessionCookie: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - name: X-Step-Up-Token
          in: header
          required: false
          description: Required only when all_sessions would revoke another active session.
          schema: { type: string, pattern: '^sup_[A-Za-z0-9_-]{43}$' }
        - name: X-CSRF-Token
          in: header
          required: false
          description: Required only with sessionCookie authentication; it must exactly match the cookie-bound CSRF token.
          schema: { type: string, minLength: 32, maxLength: 512 }
      requestBody:
        required: false
        content:
          application/json:
            schema: { $ref: '#/components/schemas/LogoutRequest' }
      responses:
        '204':
          description: Selected families revoked and application logout events queued.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }

  /api/v1/me:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Carbon account]
      operationId: getCurrentCarbon
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Current Carbon including self-only contact details.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/CarbonSelf' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
    patch:
      tags: [Carbon account]
      operationId: updateCurrentCarbon
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/CarbonProfilePatch' }
      responses:
        '200':
          description: Profile updated.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CarbonSelf' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/me/sessions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Carbon account]
      operationId: listCurrentCarbonSessions
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Active and recently revoked session families.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SessionPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /api/v1/me/sessions/{session_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    delete:
      tags: [Carbon account]
      operationId: revokeCurrentCarbonSession
      description: >-
        Revokes one active session after it is at least 12 hours old. Revoking a
        different session also requires the authenticating session to be at
        least 12 hours old. X-Step-Up-Token must carry a verified-channel
        account.session_revoke assertion bound to session_id. Immediate
        current-session logout remains available through POST /api/v1/logout.
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/SessionId' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '204':
          description: Session family revoked.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/me/login-history:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Carbon account]
      operationId: listCurrentCarbonLoginHistory
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: User-wide and application login events.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LoginEventPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /api/v1/silicon-auth/token:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Carbon authentication, Silicons]
      operationId: authenticateSilicon
      security: []
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SiliconAuthenticationRequest' }
      responses:
        '200':
          description: Independently generated Silicon access and refresh tokens.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/IamTokenResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organization-ids/{org_id}/availability:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Organizations]
      operationId: checkOrganizationIdAvailability
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/OrgIdPath' }]
      responses:
        '200':
          description: Current availability; no reservation is created.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Availability' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Organizations]
      operationId: listOrganizations
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: status, in: query, schema: { type: string, enum: [active, removed] } }
      responses:
        '200':
          description: Organizations for the authenticated Carbon; Silicons are forbidden.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/OrganizationPage' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    post:
      tags: [Organizations]
      operationId: createOrganization
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OrganizationCreate' }
      responses:
        '201':
          description: Organization and sole owner membership created atomically.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Organization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}:
    parameters: [{ $ref: '#/components/parameters/OrgIdPath' }, { $ref: '#/components/parameters/TestingEnvironmentSelection' }]
    get:
      tags: [Organizations]
      operationId: getOrganization
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Non-secret organization configuration visible to an active member.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/Organization' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    patch:
      tags: [Organizations]
      operationId: updateOrganization
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/OrganizationPatch' }
      responses:
        '200':
          description: Organization updated; org_id is immutable.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Organization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/ownership-transfers:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Organizations, Governance]
      operationId: transferOrganizationOwnership
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OwnershipTransfer' }
      responses:
        '200':
          description: Owner swapped atomically; the previous owner becomes an admin with no delegated capabilities.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Organization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }

  /api/v1/organizations/{org_id}/members:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Memberships]
      operationId: listOrganizationMembers
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: principal_type, in: query, schema: { type: string, enum: [carbon, silicon] } }
        - { name: tag_id, in: query, schema: { type: string, format: uuid } }
        - { name: status, in: query, schema: { type: string, enum: [active, removed] } }
      responses:
        '200':
          description: Typed membership directory page.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/MembershipPage' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/v1/organizations/{org_id}/members/{membership_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/MembershipIdPath' }
    get:
      tags: [Memberships]
      operationId: getOrganizationMember
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Membership directory record.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/Membership' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [Memberships]
      operationId: updateOrganizationMemberDirectory
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/MembershipDirectoryPatch' }
      responses:
        '200':
          description: First Silicon, extra Silicons, profile data, or reporting line updated; tags and job_role use governed request workflows.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Membership' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    delete:
      tags: [Memberships]
      operationId: removeOrganizationMember
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
        - name: reassign_reports_to
          in: query
          description: Required replacement Silicon membership when removing a Silicon with direct reports.
          schema: { type: string, format: uuid }
      responses:
        '204':
          description: Authority revoked immediately; membership identity and history are retained.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/members/{membership_id}/authorization:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/MembershipIdPath' }
    get:
      tags: [Memberships, Governance]
      operationId: getOrganizationMemberAuthorization
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Organization tier, delegated capabilities, and authorization epoch.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/MembershipAuthorization' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/v1/organizations/{org_id}/members/{membership_id}/admin-promotions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Memberships, Governance]
      operationId: promoteOrganizationMemberToAdmin
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '200':
          description: Active Carbon member promoted to admin with no implicit capabilities.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MembershipAuthorization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/members/{membership_id}/admin-demotions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Memberships, Governance]
      operationId: demoteOrganizationAdmin
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '200':
          description: Admin demoted to member and all organization capabilities revoked.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MembershipAuthorization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/members/{membership_id}/capabilities:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    put:
      tags: [Memberships, Governance]
      operationId: replaceOrganizationMemberCapabilities
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OrganizationCapabilitiesReplace' }
      responses:
        '200':
          description: Explicit organization capabilities replaced without changing org_role.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MembershipAuthorization' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/directory/self:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Memberships]
      operationId: getOrganizationDirectorySelf
      description: >-
        Read-only directory projection. Human/Silicon bearers and authenticated
        Application access tokens may use this endpoint. Application tokens are
        resolved through the token subject's active membership and cannot call
        directory or governance mutation endpoints.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/DirectoryFields' }
      responses:
        '200':
          description: The requester's active organization-directory projection. Trust is evaluated only for Carbon-to-Silicon or Silicon-to-Silicon and is null for rows with no defined direction.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/DirectoryMember' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/organizations/{org_id}/directory/members:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Memberships]
      operationId: listOrganizationDirectoryMembers
      description: >-
        Read-only directory listing. Human/Silicon bearers and authenticated
        Application access tokens may use this endpoint; Application tokens are
        never accepted by mutation routes.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { $ref: '#/components/parameters/DirectoryFields' }
      responses:
        '200':
          description: Active team members projected from the requester's point of view; trust is null for Carbon rows because Carbon-to-Carbon and Silicon-to-Carbon are undefined.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/DirectoryPage' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/organizations/{org_id}/directory/members/{membership_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Memberships]
      operationId: getOrganizationDirectoryMember
      description: >-
        Read-only directory member projection. Authenticated Application access
        tokens may read role, tags, organization metadata and evaluated trust,
        but cannot mutate directory or governance state.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/DirectoryFields' }
      responses:
        '200':
          description: One active team member projected from the requester's point of view; trust is null when the requester-to-row direction is undefined.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/DirectoryMember' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }

  /api/v1/organizations/{org_id}/carbon-invites:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Invitations]
      operationId: listCarbonInvitations
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: status, in: query, schema: { $ref: '#/components/schemas/InviteStatus' } }
      responses:
        '200':
          description: Invitations visible to the owner or callers with members.invite.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvitePage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    post:
      tags: [Invitations]
      operationId: createCarbonInvitation
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CarbonInviteCreate' }
      responses:
        '201':
          description: 48-hour invitation created for an email recipient or existing Carbon; org_role is always member.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Invite' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/carbon-invites/{invite_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/InviteIdPath' }
    get:
      tags: [Invitations]
      operationId: getCarbonInvitation
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Invitation with masked target identity.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Invite' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Invitations]
      operationId: revokeCarbonInvitation
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Pending invitation and its OTP challenges revoked.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/join/email-verification-code:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Invitations]
      operationId: initiateEmailInvitationJoin
      description: >-
        An authenticated direct Carbon submits the invited email. The service
        verifies that it is the exact active verified address immutably bound
        when that Carbon's invitation was created and that a pending unexpired
        invitation exists for this active email-join organization, then creates
        a pending challenge and sends the six-digit Postmark OTP outside the
        database transaction. The challenge becomes deliverable and the
        endpoint succeeds only after provider confirmation. The
        response exposes only the invitation ID needed by the join request and
        the code lifetime, never contact data. Ten attempts per Carbon and
        organization, including misses and varied emails, start a complete
        60-second cooldown. A successful idempotent replay does not consume an
        additional attempt.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EmailInput' }
      responses:
        '202':
          description: Matching invitation resolved and Postmark confirmed delivery acceptance for its replacement code.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvitationEmailCodeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404':
          description: The authenticated Carbon has no matching usable invitation (`not_invited`).
          content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/organizations/{org_id}/join:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Invitations, Memberships]
      operationId: joinOrganizationByEmailInvite
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvitationAcceptance' }
      responses:
        '200':
          description: Membership created or deliberately reactivated with the invitation directory and trust snapshot applied atomically.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Membership' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organizations/{org_id}/silicons:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Silicons]
      operationId: listOrganizationSilicons
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: tag_id, in: query, schema: { type: string, format: uuid } }
      responses:
        '200':
          description: Active Silicons in the organization.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/SiliconPage' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    post:
      tags: [Silicons]
      operationId: createSilicon
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SiliconCreate' }
      responses:
        '201':
          description: Active Silicon profile and 32-hex-character raw token created; webhook configuration is independent.
          headers:
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconCreated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/silicons/{silicon_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/SiliconIdPath' }
    get:
      tags: [Silicons]
      operationId: getSilicon
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Silicon profile, immutable directory identity, and hierarchy level.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/Silicon' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [Silicons]
      operationId: updateSilicon
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/SiliconPatch' }
      responses:
        '200':
          description: Mutable profile or hierarchy fields updated; public IDs and job role remain unchanged.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Silicon' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
    delete:
      tags: [Silicons]
      operationId: removeSilicon
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
        - name: reassign_reports_to
          in: query
          description: Required replacement Silicon when direct reports exist.
          schema: { type: string, format: uuid }
      responses:
        '204':
          description: 'Silicon disabled, credentials revoked, reports reassigned, and events queued.'
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/silicons/{silicon_id}/webhook:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/SiliconIdPath' }
    get:
      tags: [Silicons, Webhooks]
      operationId: getSiliconWebhook
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Active subscriber-managed endpoint. The signing secret is never returned by GET.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconWebhook' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
    put:
      tags: [Silicons, Webhooks]
      operationId: replaceSiliconWebhook
      description: >-
        Configures or replaces the HTTPS endpoint and rotates its signing secret.
        A Carbon manager must also present verified-channel step-up. If an active
        endpoint already exists, If-Match is required; initial creation has no
        existing representation and may omit it.
      x-carbon-step-up-assurance: verified_channel
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - name: If-Match
          in: header
          required: false
          description: Strong current endpoint ETag; required when replacing an existing endpoint.
          schema: { type: string, pattern: '^"[0-9]+"$' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SiliconWebhookReplace' }
      responses:
        '200':
          description: Endpoint active; the new HMAC secret is shown only in this no-store response.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconWebhookConfigured' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    delete:
      tags: [Silicons, Webhooks]
      operationId: deleteSiliconWebhook
      description: Disables the endpoint, retires its signing keys, and removes its subscription.
      x-carbon-step-up-assurance: verified_channel
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Endpoint disabled and subscription removed.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }

  /api/v1/organizations/{org_id}/silicons/{silicon_id}/webhook/dead-letters:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Silicons, Webhooks]
      operationId: listSiliconWebhookDeadLetters
      description: Lists only dead letters currently visible to this Silicon recipient or its authorized Carbon manager.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/SiliconIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Recipient-scoped dead-letter page in reverse dead-letter order.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WebhookDeadLetterPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/silicons/{silicon_id}/webhook/dead-letters/replays:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Silicons, Webhooks]
      operationId: replaySiliconWebhookDeadLetters
      description: >-
        Reauthorizes the current endpoint and subscription, then requeues up to
        100 exact deliveries in original event order using the current URL and
        signing secret. Original event identity, payload, occurrence time,
        aggregate version, and attempt history are preserved.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/SiliconIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WebhookReplayRequest' }
      responses:
        '202':
          description: Authorized dead letters were reset for ordered redelivery.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WebhookReplayResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organizations/{org_id}/silicons/{silicon_id}/webhook/subscription:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/SiliconIdPath' }
    get:
      tags: [Silicons, Webhooks]
      operationId: getSiliconWebhookSubscription
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Active topic and tag-filter subscription.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconWebhookSubscription' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
    put:
      tags: [Silicons, Webhooks]
      operationId: replaceSiliconWebhookSubscription
      description: >-
        Creates or replaces the subscription. An active endpoint must already
        exist. A Carbon manager must also present verified-channel step-up. If a
        subscription already exists, If-Match is required; initial creation may
        omit it.
      x-carbon-step-up-assurance: verified_channel
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - name: If-Match
          in: header
          required: false
          description: Strong current subscription ETag; required when replacing an existing subscription.
          schema: { type: string, pattern: '^"[0-9]+"$' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SiliconWebhookSubscriptionReplace' }
      responses:
        '200':
          description: Canonical subscription created or replaced.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconWebhookSubscription' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    delete:
      tags: [Silicons, Webhooks]
      operationId: deleteSiliconWebhookSubscription
      description: Removes the subscription without changing the configured endpoint.
      x-carbon-step-up-assurance: verified_channel
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Subscription removed; delivery stops.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/silicons/{silicon_id}/token-rotation-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Silicons, Governance]
      operationId: requestSiliconTokenRotation
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/SiliconIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '201':
          description: Immutable owner-approval request created.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/silicons/{silicon_id}/token-rotation-requests/{request_id}/complete:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Silicons, Governance]
      operationId: completeSiliconTokenRotation
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/SiliconIdPath' }
        - { $ref: '#/components/parameters/ApprovalRequestIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: >-
            Generates and reveals the replacement credential after approval has
            already invalidated the old credential and revoked its session and
            token authority.
          headers:
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SiliconTokenRotated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organizations/{org_id}/tags:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Tags]
      operationId: listOrganizationTags
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Normalized organization-scoped tags.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TagPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    post:
      tags: [Tags]
      operationId: createOrganizationTag
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TagCreate' }
      responses:
        '201':
          description: Tag created.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tag' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/tags/{tag_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TagIdPath' }
    get:
      tags: [Tags]
      operationId: getOrganizationTag
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Tag.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tag' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [Tags]
      operationId: updateOrganizationTag
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/TagPatch' }
      responses:
        '200':
          description: Tag renamed without changing its stable identifier.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tag' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
    delete:
      tags: [Tags]
      operationId: deleteOrganizationTag
      security: [{ iamBearer: [] }]
      description: >-
        Deletes a tag. Requires `tags.manage`, which owners hold implicitly and
        admins by grant. The tag stops existing for every caller: it leaves
        listings and member projections immediately, stops conferring Silicon
        access, and its name becomes available again. The cascade is atomic --
        assignments are removed from every member who held it, tag-scoped trust
        rules are archived, and the affected members' authorization epochs
        advance so cached authority is invalidated at once. The removal is
        recorded in each affected member's tag history and published as
        `organization.tag_archived.v1`.
      parameters:
        - { $ref: '#/components/parameters/IfMatch' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '204':
          description: Tag deleted.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/tags/{tag_id}/members:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Tags, Memberships]
      operationId: listOrganizationTagMembers
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/TagIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Carbon and Silicon memberships assigned to the tag.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/MembershipPage' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  /api/v1/organizations/{org_id}/trust/default:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Trust]
      operationId: getOrganizationDefaultTrust
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/OrgIdPath' }]
      responses:
        '200':
          description: Organization default trust, initially internal/not_trusted.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustValue' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    put:
      tags: [Trust]
      operationId: replaceOrganizationDefaultTrust
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TrustValue' }
      responses:
        '200':
          description: Advisory default replaced.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustValue' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/trust/rules:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Trust]
      operationId: listTrustRules
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Typed advisory trust rules.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustRulePage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    post:
      tags: [Trust]
      operationId: createTrustRule
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TrustRuleCreate' }
      responses:
        '201':
          description: Advisory rule created.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustRule' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/trust/rules/{rule_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TrustRuleIdPath' }
    get:
      tags: [Trust]
      operationId: getTrustRule
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Trust rule.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustRule' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    patch:
      tags: [Trust]
      operationId: updateTrustRule
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/TrustRulePatch' }
      responses:
        '200':
          description: Trust rule updated.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustRule' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
    delete:
      tags: [Trust]
      operationId: deleteTrustRule
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Rule deleted.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/trust/effective:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Trust]
      operationId: evaluateEffectiveTrust
      description: >-
        Uses a Carbon subject's membership-wide default or the organization
        default for a Silicon subject, then applies tag and exact-Silicon rules.
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/OrgIdPath' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TrustEvaluationRequest' }
      responses:
        '200':
          description: Advisory value and matching-rule explanation; never an authorization decision.
          content:
            application/json:
              schema: { anyOf: [{ $ref: '#/components/schemas/TrustEvaluation' }, { $ref: '#/components/schemas/ApplicationReadObject' }] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }

  /api/v1/organizations/{org_id}/role-change-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Governance]
      operationId: createRoleChangeRequest
      description: Only an active Silicon may create this governed request; regular Carbons use no request path.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RoleChangeRequestCreate' }
      responses:
        '201':
          description: Immutable approval request created with target-specific quorum.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/organizations/{org_id}/approval-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Governance]
      operationId: listApprovalRequests
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: status, in: query, schema: { $ref: '#/components/schemas/ApprovalStatus' } }
        - { name: kind, in: query, schema: { $ref: '#/components/schemas/ApprovalKind' } }
        - { name: actionable_by_me, in: query, schema: { type: boolean } }
      responses:
        '200':
          description: Pending and historical requests visible to the caller.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequestPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/v1/organizations/{org_id}/approval-requests/{request_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Governance]
      operationId: getApprovalRequest
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/ApprovalRequestIdPath' }
      responses:
        '200':
          description: Immutable request and collected decisions.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/v1/organizations/{org_id}/approval-requests/{request_id}/decisions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Governance]
      operationId: decideApprovalRequest
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/ApprovalRequestIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
        - name: X-Step-Up-Token
          in: header
          required: false
          description: Required with verified-channel assurance when deciding a Silicon token-rotation request.
          schema: { type: string, pattern: '^sup_[A-Za-z0-9_-]{43}$' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApprovalDecisionCreate' }
      responses:
        '200':
          description: >-
            Unique decision recorded. Terminal job-role/tag changes apply
            exactly once. Approval of a Silicon credential rotation immediately
            invalidates the old credential and its token/session authority but
            does not generate the replacement secret.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/members/{membership_id}/job-role:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    put:
      tags: [Governance]
      operationId: replaceMemberJobRole
      description: >-
        A Carbon owner, or an admin with roles.approve, directly replaces the
        descriptive job role for any active Carbon or Silicon and appends
        immutable direct-actor history.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/DirectJobRoleReplace' }
      responses:
        '200':
          description: Job role replaced directly and history appended.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Membership' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/organizations/{org_id}/members/{membership_id}/job-role-history:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Governance]
      operationId: listMemberJobRoleHistory
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Applied job-role changes and their approvers.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RoleHistoryPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  /api/v1/organizations/{org_id}/members/{membership_id}/tag-change-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Governance]
      operationId: createMemberTagChangeRequest
      description: Only an active Silicon may request this tag-set change for an active Carbon or Silicon target.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TagChangeRequestCreate' }
      responses:
        '201':
          description: >-
            Immutable tag-change request created. Carbon targets require the
            affected Carbon and an eligible owner/admin; Silicon targets
            require an eligible owner/admin.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApprovalRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organizations/{org_id}/members/{membership_id}/tags:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    put:
      tags: [Governance, Tags]
      operationId: replaceMemberTags
      description: >-
        A Carbon owner, or an admin with tags.manage, atomically replaces the
        complete active tag set for any active Carbon or Silicon, advances its
        authorization epoch, and appends immutable direct-actor history.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/DirectTagSetReplace' }
      responses:
        '200':
          description: Complete membership tag set replaced directly.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Membership' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/organizations/{org_id}/members/{membership_id}/tag-history:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Governance]
      operationId: listMemberTagHistory
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/MembershipIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Applied membership tag sets with their requester and approvers.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TagHistoryPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  /api/v1/organizations/{org_id}/testing-environments:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
    get:
      tags: [Testing environments]
      operationId: listTestingEnvironments
      security: [{ iamBearer: [] }]
      description: Lists the organization's environments. Deleted environments are hidden unless `status` asks for them.
      parameters:
        - { $ref: '#/components/parameters/TestingEnvironmentStatus' }
        - { $ref: '#/components/parameters/TestingEnvironmentCursor' }
        - { $ref: '#/components/parameters/TestingEnvironmentLimit' }
      responses:
        '200':
          description: Environments owned by the organization.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    post:
      tags: [Testing environments]
      operationId: createTestingEnvironment
      security: [{ iamBearer: [] }]
      description: >-
        Creates an empty environment. Any active member may create one, Carbon
        or Silicon, and becomes its creator with permanent administrative
        authority over it. The key is returned here for convenience and stays
        retrievable afterwards.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TestingEnvironmentCreate' }
      responses:
        '201':
          description: Environment created; the response carries its key.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentWithKey' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/testing-environments/{environment_id}:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TestingEnvironmentIdPath' }
    get:
      tags: [Testing environments]
      operationId: getTestingEnvironment
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Environment.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironment' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    patch:
      tags: [Testing environments]
      operationId: updateTestingEnvironment
      security: [{ iamBearer: [] }]
      description: Renames or re-describes a live environment. Restricted to its creator and to organization owners and admins.
      parameters:
        - { $ref: '#/components/parameters/IfMatch' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TestingEnvironmentPatch' }
      responses:
        '200':
          description: Environment updated.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironment' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    delete:
      tags: [Testing environments]
      operationId: deleteTestingEnvironment
      security: [{ iamBearer: [] }]
      description: >-
        Retires the environment. Nothing is erased yet: the record survives with
        a `purge_after` deadline and can be restored until it passes, after
        which the data is destroyed permanently.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Environment retired and scheduled for purge.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironment' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/testing-environments/{environment_id}/key:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TestingEnvironmentIdPath' }
    get:
      tags: [Testing environments]
      operationId: getTestingEnvironmentKey
      security: [{ iamBearer: [] }]
      description: >-
        Returns the current environment key. Restricted to the environment's
        creator and to organization owners and admins, and audited on every
        read.
      responses:
        '200':
          description: Current environment key.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentKey' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/testing-environments/{environment_id}/key-rotations:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TestingEnvironmentIdPath' }
    post:
      tags: [Testing environments]
      operationId: rotateTestingEnvironmentKey
      security: [{ iamBearer: [] }]
      description: Issues a new key and invalidates the previous one immediately.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Key rotated; the response carries the new key.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentWithKey' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/testing-environments/{environment_id}/cleanings:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TestingEnvironmentIdPath' }
    post:
      tags: [Testing environments]
      operationId: cleanTestingEnvironment
      security: [{ iamBearer: [] }]
      description: >-
        Deletes every row the environment holds while keeping the environment
        itself. The key is unchanged and the environment stays usable.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Environment data erased.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentCleaning' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/testing-environments/{environment_id}/restorations:
    parameters:
      - { $ref: '#/components/parameters/OrgIdPath' }
      - { $ref: '#/components/parameters/TestingEnvironmentIdPath' }
    post:
      tags: [Testing environments]
      operationId: restoreTestingEnvironment
      security: [{ iamBearer: [] }]
      description: >-
        Brings a retired environment back before its `purge_after` deadline.
        Conflicts when the name has been taken by another environment in the
        meantime, or when the deadline has already passed.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Environment restored.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironment' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/testing-environment:
    get:
      tags: [Testing environments]
      operationId: describeCurrentTestingEnvironment
      security: [{ testingEnvironmentKey: [] }]
      description: >-
        Describes the environment the presented key opens. Authorized by the
        key alone, so it needs no IAM identity and discloses nothing about the
        owning organization's members or production state.
      responses:
        '200':
          description: The environment this key opens.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentSelf' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/testing-environment/cleanings:
    post:
      tags: [Testing environments]
      operationId: cleanCurrentTestingEnvironment
      security: [{ testingEnvironmentKey: [] }]
      description: >-
        Deletes every row the environment holds, authorized by the key alone.
        The key is the environment's root authority, and the data is disposable
        by construction.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Environment data erased.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingEnvironmentCleaning' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/testing-environment/applications/imports:
    post:
      tags: [Testing environments, Applications]
      operationId: importTestingApplication
      security: [{ iamBearer: [], testingEnvironmentKey: [] }]
      description: >-
        Imports one canonical production Application into the selected testing
        environment. If its organization is absent there, IAM creates it and
        makes the authenticated test Carbon its owner. The Application keeps
        its production app_id, base_url, webhook URL, OBO surface, and inherited
        webhook signing secret; that inherited secret is never returned. IAM
        issues and returns a fresh test-only client secret.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TestingApplicationImport' }
      responses:
        '201':
          description: Production Application imported with a fresh test-only client secret.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestingApplicationImported' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /api/v1/organizations/{org_id}/sso:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [SSO]
      operationId: getSsoConfiguration
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/OrgIdPath' }]
      responses:
        '200':
          description: Non-secret WorkOS mapping, entitlement, connection, and join-method state.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SsoConfiguration' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [SSO]
      operationId: disableSsoConfiguration
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Connection disabled after join_method is safely changed away from sso.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/organizations/{org_id}/sso/setup-link:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [SSO]
      operationId: createSsoSetupLink
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '201':
          description: Five-minute WorkOS Admin Portal setup link. The idempotency reservation commits before provider I/O; an in-flight identical key returns a retryable conflict and a completed response is replayable for five minutes.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SsoSetupLink' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '502': { $ref: '#/components/responses/BadGateway' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/organizations/{org_id}/sso/authorize:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [SSO]
      operationId: authorizeSsoJoin
      security: [{ sessionCookie: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { name: return_to, in: query, schema: { type: string, format: uri } }
      responses:
        '302':
          description: Validated redirect to WorkOS with state and nonce bound to the authenticated Carbon.
          headers: { Location: { schema: { type: string, format: uri } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '502': { $ref: '#/components/responses/BadGateway' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/sso/callback:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [SSO]
      operationId: handleSsoCallback
      security: [{ sessionCookie: [] }]
      parameters:
        - { name: code, in: query, required: true, schema: { type: string, minLength: 1, maxLength: 2048 } }
        - { name: state, in: query, required: true, schema: { type: string, minLength: 16, maxLength: 512 } }
      responses:
        '302':
          description: Existing Carbon admitted or linked through the active tenant-bound WorkOS connection, using fixed conservative member defaults on admission or reactivation, then redirected only to the stored validated return URI.
          headers: { Location: { schema: { type: string, format: uri } } }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '502': { $ref: '#/components/responses/BadGateway' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/organizations/{org_id}/sso/test:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [SSO]
      operationId: testSsoConfiguration
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      responses:
        '200':
          description: Reads the exact provider organization and connection; succeeds only when the connection is active and belongs to the permanently mapped organization.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TestResult' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '502': { $ref: '#/components/responses/BadGateway' }
        '504': { $ref: '#/components/responses/GatewayTimeout' }
  /api/v1/provider-webhooks/workos:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [SSO, Webhooks]
      operationId: receiveWorkOsWebhook
      security: [{ workosSignature: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: true, maxProperties: 100 }
      responses:
        '202': { description: 'Signature-valid, fresh, replay-safe event accepted idempotently.' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '413': { $ref: '#/components/responses/PayloadTooLarge' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/applications:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplications
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: status, in: query, schema: { $ref: '#/components/schemas/ApplicationStatus' } }
      responses:
        '200':
          description: Applications owned by organizations in which the authenticated Carbon is a current active owner/admin.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    post:
      tags: [Applications]
      operationId: createApplication
      security: [{ iamBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/IdempotencyKey' }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationCreate' }
      responses:
        '201':
          description: >-
            Verified organization-owned Application with canonical
            `{org_id}>{handle}` id and discoverable base URL. IAM generates the
            app secret and stores the caller-supplied webhook secret. The
            response echoes the supplied webhook secret for v1 compatibility.
            Current owner/admin authority is rechecked before creation or
            secret replay.
          headers:
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationCreated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/application-directory/{app_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: discoverApplicationBaseUrl
      description: >-
        Returns the public backend base URL of a verified Application. Any
        authenticated Application may discover any other Application,
        including one outside its organization. The caller is derived only
        from HTTP Basic credentials. With an X-Testing-Environment-Key, both
        the caller credential and target resolve exclusively inside that
        environment.
      security: [{ applicationBasic: [] }]
      parameters: [{ $ref: '#/components/parameters/AppIdPath' }]
      responses:
        '200':
          description: Canonical Application id and its configured base URL.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBaseUrl' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}:
    parameters: [{ $ref: '#/components/parameters/AppIdPath' }, { $ref: '#/components/parameters/TestingEnvironmentSelection' }]
    get:
      tags: [Applications]
      operationId: getApplication
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Organization-owned manageable application without raw secrets.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Application' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    patch:
      tags: [Applications]
      operationId: updateApplication
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema: { $ref: '#/components/schemas/ApplicationPatch' }
      responses:
        '200':
          description: Non-sensitive metadata updated; scope or redirect changes enter review before activation.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Application' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/client-secret-rotations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Applications]
      operationId: rotateApplicationClientSecret
      description: >-
        Atomically retires every prior usable client secret and reveals exactly
        one active successor. Step-up action application.client_secret.rotate
        is bound to the internal Application UUID. Exact idempotent replay may
        recover the no-store response for ten minutes.
      x-required-step-up-assurance: verified_channel
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '200':
          description: Client secret rotated and shown once.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationSecretRotated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/webhook-secret-rotations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Applications, Webhooks]
      operationId: rotateApplicationWebhookSecret
      description: >-
        Atomically retires the current webhook signing secret and installs the
        caller-supplied successor. New deliveries use the returned secret version;
        already persisted in-flight deliveries retain their original signed
        bytes and version. Exact idempotent replay may recover the no-store
        response for ten minutes. Step-up action
        application.webhook_secret.rotate is bound to the internal
        Application UUID.
      x-required-step-up-assurance: verified_channel
      security: [{ iamBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationWebhookSecretRotate' }
      responses:
        '200':
          description: Caller-supplied webhook signing secret installed; the response echoes it for v1 compatibility.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationWebhookSecretRotated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/webhook:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications, Webhooks]
      operationId: getApplicationWebhook
      description: >-
        Available to the owning organization's current Carbon owner/admin
        or an IAM platform administrator with applications.review. Includes
        the internal Application UUID used to bind webhook-approval step-up.
      security: [{ iamBearer: [] }, { platformAdminBearer: [] }]
      parameters: [{ $ref: '#/components/parameters/AppIdPath' }]
      responses:
        '200':
          description: One active destination plus any pending reviewed replacement. The body version and ETag are the same application aggregate version.
          headers: { ETag: { $ref: '#/components/headers/ETag' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationWebhook' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    put:
      tags: [Applications, Webhooks]
      operationId: proposeApplicationWebhookReplacement
      description: >-
        In production, validates and proposes a replacement for approval by
        the owning organization's current owner/admin or an IAM platform
        administrator with applications.review, while the current endpoint
        remains active. In a testing
        environment, which deliberately has no platform reviewer, atomically
        retires the old endpoint and activates this replacement immediately.
        Repeating the active or pending URL without a replacement secret, or
        supplying the exact current URL and secret where they can be compared,
        returns `409 application_webhook_unchanged` without changing the
        Application version or emitting audit/outbox events.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationWebhookReplace' }
      responses:
        '200':
          description: >-
            Testing-environment replacement activated immediately. When an
            imported test Application still inherits its production signing
            key, the request must provide a new test-only signing secret and
            this response echoes it for v1 compatibility.
            The body version and ETag are the same Application aggregate
            version.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control:
              schema: { const: no-store }
              description: Present when the response echoes a caller-supplied signing secret.
            Pragma:
              schema: { const: no-cache }
              description: Present when the response echoes a caller-supplied signing secret.
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationWebhook' }
        '202':
          description: >-
            HTTPS destination passed SSRF validation and awaits webhook
            approval. Normally the active destination and signing secret are
            unchanged. If the request supplies a replacement secret, IAM
            installs it for the pending endpoint and echoes it for v1
            compatibility. The body version and ETag are the same Application
            aggregate version.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Cache-Control:
              schema: { const: no-store }
              description: Present when the response echoes a caller-supplied signing secret.
            Pragma:
              schema: { const: no-cache }
              description: Present when the response echoes a caller-supplied signing secret.
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationWebhook' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/webhook/approvals:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Applications, Webhooks]
      operationId: approveApplicationWebhook
      description: >-
        Activates the pending webhook destination of an already verified
        Application, retiring its previous active endpoint if present.
        Authorized for the owning organization's current Carbon owner/admin
        or an IAM platform administrator with applications.review. The
        creator is audit metadata, not an independent source of authority.
        Requires verified-channel step-up action application.webhook.approve
        bound to the internal Application UUID, the current Application
        If-Match version, and an Idempotency-Key. No request fields are needed;
        omit the body or send an empty JSON object.
        This narrow operation changes neither Application status nor scopes.
        A newly registered verified Application's first pending webhook is
        eligible; an Application itself still under_review requires platform
        application review. Missing pending endpoints return 409
        application_webhook_no_pending_endpoint; non-verified Applications
        return 409 application_webhook_approval_state_conflict. Test endpoints
        normally activate immediately and therefore have no pending endpoint
        to approve. IAM revalidates the destination's public HTTPS/DNS safety
        at approval; an unsafe destination returns 422 without activation.
      x-required-step-up-assurance: verified_channel
      security:
        - { iamBearer: [], stepUpToken: [] }
        - { platformAdminBearer: [], stepUpToken: [] }
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: false
        content:
          application/json:
            schema: { type: object, additionalProperties: false }
      responses:
        '200':
          description: Pending webhook activated. The body version and ETag are the same Application aggregate version; no secret is returned.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationWebhook' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/webhook/dead-letters:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications, Webhooks]
      operationId: listApplicationWebhookDeadLetters
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Application-recipient dead-letter page in reverse dead-letter order.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WebhookDeadLetterPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/webhook/dead-letters/replays:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Applications, Webhooks]
      operationId: replayApplicationWebhookDeadLetters
      description: >-
        Reauthorizes current Application access, then requeues up to 100 exact
        deliveries in original event order using the current endpoint and
        signing secret while preserving immutable event and attempt history.
        The secret-free session.logout.v1 revocation-control event instead
        requires the exact persisted dead-letter recipient to remain bound to
        this Application because logout itself revoked the delegated authority.
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WebhookReplayRequest' }
      responses:
        '202':
          description: Authorized dead letters were reset for ordered redelivery.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WebhookReplayResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/applications/{app_id}/login-history:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplicationLoginHistory
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: App-specific authorization and token-exchange history.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LoginEventPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/obo-access/applications/{app_id}/endpoints:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [OBO Access]
      operationId: discoverApplicationOboEndpoints
      description: >-
        Returns the active callable endpoint catalog of a verified Application
        in the authenticated Application's organization. Nonexistent,
        unverified, and cross-organization targets all return the same 404.
      security: [{ applicationBasic: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
      responses:
        '200':
          description: Same-organization Application reference and its active endpoints in endpoint_id order.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OboEndpointCatalog' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/obo-access/exchanges:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [OBO Access]
      operationId: exchangeOboAccessProof
      description: >-
        Authenticates App A with HTTP Basic and a fresh request HMAC. IAM
        derives the tenant from App A, requires audience App B and the subject
        membership to belong to that same organization, loads registered_path
        from the selected active endpoint, and verifies X-OBO-Signature in
        constant time over
        timestamp.method.registered_path.body_sha256.Idempotency-Key. The
        request carries only metadata and the body digest, never the downstream
        body or file. Exact idempotent replay expires no later than the proof.
      security: [{ applicationBasic: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/OboTimestamp' }
        - { $ref: '#/components/parameters/OboSignature' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OboExchangeRequest' }
      responses:
        '201':
          description: Random single-use proof bound to same-organization source, audience, subject token, actor, registered endpoint, method, path, body digest, and exact metadata; valid for at most 60 seconds.
          headers:
            Cache-Control: { schema: { const: no-store } }
            Pragma: { schema: { const: no-cache } }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OboProofResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/obo-access/verify:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [OBO Access]
      operationId: consumeOboAccessProof
      description: >-
        App B submits the proof and the method, path, and body digest calculated
        from the actual downstream request. IAM verifies every binding before
        atomically consuming the proof. Successful verification is never stored
        or replayed; exactly one concurrent call succeeds and later use returns
        409. No Idempotency-Key, X-Org-ID, or actual request body is accepted.
      security: [{ applicationBasic: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OboVerifyRequest' }
      responses:
        '200':
          description: Proof consumed exactly once; returns its bound actor, endpoint, and exact metadata for audience-side authorization.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OboAccessResult' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '410': { $ref: '#/components/responses/Gone' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /api/v1/admin/applications:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Platform administration]
      operationId: adminListApplications
      security: [{ platformAdminBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
        - { name: status, in: query, schema: { $ref: '#/components/schemas/ApplicationStatus' } }
      responses:
        '200':
          description: Platform review queue and application inventory.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AdminApplicationPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/admin/applications/{app_id}/decisions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    post:
      tags: [Platform administration]
      operationId: adminDecideApplication
      x-required-step-up-assurance: verified_channel
      security: [{ platformAdminBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/AppIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationAdminDecision' }
      responses:
        '200':
          description: Review, suspension, reactivation, deletion, consent setting, or pending configuration decision applied.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AdminApplication' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /api/v1/admin/organizations/{org_id}/sso-entitlement:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    put:
      tags: [Platform administration, SSO]
      operationId: adminReplaceSsoEntitlement
      x-required-step-up-assurance: verified_channel
      security: [{ platformAdminBearer: [], stepUpToken: [] }]
      parameters:
        - { $ref: '#/components/parameters/OrgIdPath' }
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SsoEntitlement' }
      responses:
        '200':
          description: Backend-only SSO entitlement changed.
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SsoEntitlement' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '428': { $ref: '#/components/responses/PreconditionRequired' }
  /api/v1/application-scopes:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplicationScopes
      summary: Discover IAM or audience endpoint scopes
      description: >-
        Without app_id, returns only IAM permissions. With a qualified app_id,
        returns that verified, active application's published endpoint permissions,
        including both critical and noncritical permissions. A valid application
        with no published endpoints returns an empty items array. Unknown,
        unavailable, or inaccessible applications return 404; malformed IDs return
        422. Discovery remains isolated to the selected testing environment.
      security: [{ iamBearer: [] }]
      parameters:
        - { name: app_id, in: query, schema: { type: string } }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeCatalog' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-scope-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplicationScopeRequests
      summary: List critical scope review discussions
      security: [{ iamBearer: [] }]
      parameters:
        - { name: status, in: query, schema: { type: string } }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeRequestList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/applications/{app_id}/scope-requests:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: app_id, in: path, required: true, schema: { type: string } }
    post:
      tags: [Applications]
      operationId: createApplicationScopeRequests
      summary: Request critical scope approval with an initial message
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationScopeRequestCreate' }
      responses:
        '201':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeRequestList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-scope-requests/{request_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: request_id, in: path, required: true, schema: { type: string } }
    get:
      tags: [Applications]
      operationId: getApplicationScopeRequest
      summary: Read a critical scope discussion
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-scope-requests/{request_id}/messages:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: request_id, in: path, required: true, schema: { type: string } }
    post:
      tags: [Applications]
      operationId: replyApplicationScopeRequest
      summary: Reply to a critical scope discussion
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationScopeMessageCreate' }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-scope-requests/{request_id}/decisions:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: request_id, in: path, required: true, schema: { type: string } }
    post:
      tags: [Applications]
      operationId: decideApplicationScopeRequest
      summary: Approve or deny a critical scope request
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationScopeDecision' }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationScopeRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-bundles:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplicationBundles
      summary: List bundles you administer
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBundleList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
    post:
      tags: [Applications]
      operationId: createApplicationBundle
      summary: Create a bundle of applications in your organization
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationBundleCreate' }
      responses:
        '201':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBundle' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application-bundles/{bundle_id}:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: bundle_id, in: path, required: true, schema: { type: string } }
    get:
      tags: [Applications]
      operationId: getApplicationBundle
      summary: Read an application bundle
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBundle' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
    patch:
      tags: [Applications]
      operationId: updateApplicationBundle
      summary: Update a bundle
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationBundlePatch' }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBundle' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
    delete:
      tags: [Applications]
      operationId: deleteApplicationBundle
      summary: Delete a bundle while keeping its applications
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
        - { $ref: '#/components/parameters/IfMatch' }
      responses:
        '204':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/app-auth/bundles/{bundle_id}/organizations:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: bundle_id, in: path, required: true, schema: { type: string } }
    get:
      tags: [Applications]
      operationId: getBundleLoginOrganizations
      summary: Review a bundle before selecting organizations
      security: [{ iamBearer: [] }]
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationBundleLoginOrganizations' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/app-auth/bundles/{bundle_id}/short-lived-tokens:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
      - { name: bundle_id, in: path, required: true, schema: { type: string } }
    post:
      tags: [Applications]
      operationId: issueBundleShortLivedTokens
      summary: Issue separate tokens atomically for the complete bundle
      security: [{ iamBearer: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BatchLoginRequest' }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BatchLoginTokens' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /api/v1/application/testing-environments:
    parameters:
      - { $ref: '#/components/parameters/TestingEnvironmentSelection' }
    get:
      tags: [Applications]
      operationId: listApplicationTestingEnvironments
      summary: List active environments for the authenticated production application
      security: [{ applicationBasic: [] }]
      parameters:
        - { $ref: '#/components/parameters/Cursor' }
        - { $ref: '#/components/parameters/Limit' }
      responses:
        '200':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationTestingEnvironmentPage' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
    post:
      tags: [Applications]
      operationId: createApplicationTestingEnvironment
      summary: Prepare an isolated environment with all transitive application dependencies
      security: [{ applicationBasic: [] }]
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ApplicationTestingEnvironmentCreate' }
      responses:
        '201':
          description: Successful operation.
          headers:
            Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApplicationTestingEnvironmentCreated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }

  /api/v1/contracts:
    get:
      tags: [System]
      operationId: listContractVersions
      summary: List contract lifecycle states and compatibility policy
      security: []
      responses:
        '200':
          description: Current and retired contracts with idle sunset policy.
          content:
            application/json:
              schema:
                type: object
                required: [items, policy]
                properties:
                  items: { type: array, items: { type: object, additionalProperties: true } }
                  policy: { type: object, additionalProperties: true }


webhooks:
  applicationEvent:
    post:
      tags: [Webhooks]
      operationId: deliverApplicationWebhookEvent
      summary: IAM delivers a minimal versioned event to the application's one active HTTPS endpoint.
      security: []
      parameters:
        - { name: X-Silicon-IAM-Event-ID, in: header, required: true, schema: { type: string, format: uuid } }
        - { name: X-Silicon-IAM-Timestamp, in: header, required: true, schema: { type: integer } }
        - { name: X-Silicon-IAM-Key-Version, in: header, required: true, schema: { type: integer, minimum: 1 } }
        - { name: X-Silicon-IAM-Signature, in: header, required: true, schema: { type: string, pattern: '^v1=[a-f0-9]{64}$' } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - { $ref: '#/components/schemas/WebhookEvent' }
                - { $ref: '#/components/schemas/TestingWebhookEvent' }
      responses:
        '200': { description: Event accepted idempotently. }
        '202': { description: Event accepted for asynchronous local processing. }
        '204': { description: Event accepted with no response body. }
  siliconEvent:
    post:
      tags: [Webhooks]
      operationId: deliverSiliconWebhookEvent
      summary: IAM delivers subscribed organization changes to a Silicon's configured HTTPS endpoint.
      security: []
      parameters:
        - { name: X-Silicon-IAM-Event-ID, in: header, required: true, schema: { type: string, format: uuid } }
        - { name: X-Silicon-IAM-Timestamp, in: header, required: true, schema: { type: integer } }
        - { name: X-Silicon-IAM-Key-Version, in: header, required: true, schema: { type: integer, minimum: 1 } }
        - { name: X-Silicon-IAM-Signature, in: header, required: true, schema: { type: string, pattern: '^v1=[a-f0-9]{64}$' } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - { $ref: '#/components/schemas/SiliconWebhookEvent' }
                - { $ref: '#/components/schemas/TestingWebhookEvent' }
      responses:
        '200': { description: Event accepted idempotently. }
        '202': { description: Event accepted for asynchronous local processing. }
        '204': { description: Event accepted with no response body. }

components:
  securitySchemes:
    iamBearer:
      type: http
      scheme: bearer
      bearerFormat: opaque IAM access token
      description: Carbon, Silicon, or actor-bound application access token; actor class is checked per operation.
    sessionCookie:
      type: apiKey
      in: cookie
      name: iam_session
      description: Secure, HttpOnly, SameSite browser session used only by interactive authorization flows.
    applicationBasic:
      type: http
      scheme: basic
      description: Application ID as username and current versioned application secret as password.
    platformAdminBearer:
      type: http
      scheme: bearer
      bearerFormat: opaque platform-admin access token
      description: IAM token whose current Carbon principal has active platform-administrator authority.
    stepUpToken:
      type: apiKey
      in: header
      name: X-Step-Up-Token
      description: Five-minute action-bound token from the step-up verification flow.
    testingEnvironmentKey:
      type: apiKey
      in: header
      name: X-Testing-Environment-Key
      description: >-
        A 32-character alphanumeric testing environment key. Presented on any
        other route it selects that environment for the request; presented on
        the `/api/v1/testing-environment` routes it is the sole authority.
    workosSignature:
      type: apiKey
      in: header
      name: WorkOS-Signature
      description: >-
        Comma-delimited `t=<epoch_ms>,v1=<hex_hmac>` value. IAM verifies
        HMAC-SHA256 over `timestamp + '.' + exact raw UTF-8 body` in constant
        time, enforces a 300-second tolerance, and deduplicates provider event ID.
  parameters:
    TestingEnvironmentSelection:
      name: X-Testing-Environment-Key
      in: header
      required: false
      description: >-
        Optionally selects an isolated testing data plane for this otherwise
        ordinary plane-selected operation. Omit the header to use production. The value
        is the 32-character alphanumeric key issued for an active testing
        environment; it supplies selection context, not sole authorization.
      schema: { $ref: '#/components/schemas/TestingEnvironmentKeyValue' }
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Opaque caller-generated key containing 16 to 255 printable ASCII bytes. Reuse with different bytes returns idempotency_conflict.
      schema: { type: string, minLength: 16, maxLength: 255, pattern: '^[\x21-\x7E]{16,255}$' }
    OboTimestamp:
      name: X-OBO-Timestamp
      in: header
      required: true
      description: Canonical positive Unix timestamp in seconds; accepted only within 60 seconds of IAM's clock.
      schema: { type: string, pattern: '^[1-9][0-9]{0,18}$' }
    OboSignature:
      name: X-OBO-Signature
      in: header
      required: true
      description: >-
        Exactly 64 lowercase hexadecimal characters: HMAC-SHA256 using App A's
        current Application secret over
        timestamp.method.registered_path.body_sha256.Idempotency-Key.
      schema: { type: string, pattern: '^[0-9a-f]{64}$' }
    IfMatch:
      name: If-Match
      in: header
      required: true
      description: Strong ETag containing the expected aggregate version.
      schema: { type: string, pattern: '^"[0-9]+"$' }
    OrgContext:
      name: X-Org-ID
      in: header
      required: false
      description: Optional introspection organization context. An organization-bound token must agree with it; an unscoped token selects the named organization out of the user's explicitly selected active memberships, and reads inactive when it is not one of them. OBO never accepts this header.
      schema: { $ref: '#/components/schemas/OrgId' }
    SessionId:
      name: session_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    CarbonIdPath:
      name: carbon_id
      in: path
      required: true
      schema: { $ref: '#/components/schemas/CarbonId' }
    OrgIdPath:
      name: org_id
      in: path
      required: true
      schema: { $ref: '#/components/schemas/OrgId' }
    AppIdPath:
      name: app_id
      in: path
      required: true
      schema: { $ref: '#/components/schemas/AppId' }
    SiliconIdPath:
      name: silicon_id
      in: path
      required: true
      description: Global Silicon handle including the organization suffix.
      schema: { $ref: '#/components/schemas/SiliconGlobalId' }
    MembershipIdPath:
      name: membership_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    InviteIdPath:
      name: invite_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    ApprovalRequestIdPath:
      name: request_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    TagIdPath:
      name: tag_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    TrustRuleIdPath:
      name: rule_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    TestingEnvironmentIdPath:
      name: environment_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    TestingEnvironmentStatus:
      name: status
      in: query
      required: false
      description: Lifecycle filter. Defaults to `active`; `all` includes environments awaiting purge.
      schema: { type: string, enum: [active, deleted, all], default: active }
    TestingEnvironmentCursor:
      name: cursor
      in: query
      required: false
      description: Identifier of the last environment on the previous page.
      schema: { type: string, format: uuid }
    TestingEnvironmentLimit:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 100, default: 25 }
    Cursor:
      name: cursor
      in: query
      required: false
      description: Opaque continuation cursor scoped to the filters and caller.
      schema: { type: string, minLength: 1, maxLength: 2048 }
    Limit:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
    DirectoryFields:
      name: fields
      in: query
      required: false
      description: Comma-separated sparse projection. Omit it to return every directory field.
      schema:
        type: string
        minLength: 1
        pattern: '^(name|id|role|org|tags|trust)(,(name|id|role|org|tags|trust))*$'
  headers:
    RequestId:
      description: Trace identifier generated or accepted from X-Request-ID.
      schema: { type: string, minLength: 1, maxLength: 128 }
    ETag:
      description: Strong aggregate-version ETag.
      schema: { type: string, pattern: '^"[0-9]+"$' }
    RetryAfter:
      description: Seconds before retrying.
      schema: { type: integer, minimum: 1 }
    RateLimitLimit:
      description: Effective request limit for the tightest active bucket.
      schema: { type: integer, minimum: 1 }
    RateLimitRemaining:
      description: Remaining requests in that bucket.
      schema: { type: integer, minimum: 0 }
    RateLimitReset:
      description: Seconds until that bucket resets.
      schema: { type: integer, minimum: 0 }
    IdempotencyReplayed:
      description: True when this is a replay of the stored idempotent response.
      schema: { type: boolean }
  requestBodies:
    VerificationCode:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [code]
            additionalProperties: false
            properties:
              code: { $ref: '#/components/schemas/VerificationCode' }
  responses:
    HealthOk:
      description: Process is healthy for the requested probe.
      content:
        application/json:
          schema:
            type: object
            required: [ok]
            properties: { ok: { const: true } }
    CodeDispatchAccepted:
      description: >-
        Signup contact check completed. If the normalized identity already
        belongs to a Carbon, no OTP is sent and already_exists is true.
        Otherwise a ten-minute OTP is dispatched and already_exists is false.
      headers:
        X-Request-ID: { $ref: '#/components/headers/RequestId' }
        Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/CodeDispatchResult' }
    Verified:
      description: Verification completed and the OTP was consumed atomically.
      headers:
        Idempotency-Replayed: { $ref: '#/components/headers/IdempotencyReplayed' }
      content:
        application/json:
          schema:
            type: object
            required: [verified]
            properties: { verified: { const: true } }
    BadRequest:
      description: Malformed syntax, unsupported media type, or invalid protocol request.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Unauthorized:
      description: Missing, invalid, expired, or revoked authentication.
      headers: { WWW-Authenticate: { schema: { type: string } } }
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Forbidden:
      description: Authenticated principal lacks the current actor type, scope, capability, or step-up authorization.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    NotFound:
      description: Resource is absent or intentionally hidden across a tenant boundary.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Conflict:
      description: Unique, idempotency, replay, terminal-state, or concurrent-state conflict.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    NotAcceptable:
      description: The client and server do not support a common public API version.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Gone:
      description: One-time challenge, short-lived login token, invitation, proof, or response window expired.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    PreconditionFailed:
      description: If-Match does not equal the current aggregate version.
      headers: { ETag: { $ref: '#/components/headers/ETag' } }
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    PayloadTooLarge:
      description: Request body exceeds the endpoint's configured bound.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    UnprocessableEntity:
      description: Well-formed request violates field, policy, hierarchy, quorum, or tenant invariants.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    PreconditionRequired:
      description: Required Idempotency-Key, If-Match, or step-up precondition is absent.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    TooManyRequests:
      description: One or more distributed request buckets are exhausted, or an OTP challenge is inside its 60-second cooldown after ten failed verifications.
      headers:
        Retry-After: { $ref: '#/components/headers/RetryAfter' }
        RateLimit-Limit: { $ref: '#/components/headers/RateLimitLimit' }
        RateLimit-Remaining: { $ref: '#/components/headers/RateLimitRemaining' }
        RateLimit-Reset: { $ref: '#/components/headers/RateLimitReset' }
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    BadGateway:
      description: Authenticated upstream provider returned an invalid or failed response.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    ServiceUnavailable:
      description: Required local dependency or provider is temporarily unavailable.
      headers: { Retry-After: { $ref: '#/components/headers/RetryAfter' } }
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    GatewayTimeout:
      description: A bounded server or provider processing deadline elapsed.
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
  schemas:
    ErrorEnvelope:
      type: object
      required: [error]
      additionalProperties: false
      properties:
        error:
          type: object
          required: [code, message, request_id]
          additionalProperties: false
          properties:
            code: { type: string, pattern: '^[a-z][a-z0-9_]{1,63}$' }
            message: { type: string, minLength: 1, maxLength: 500 }
            details: { type: object, additionalProperties: true }
            request_id: { type: string, minLength: 1, maxLength: 128 }
    VersionInfo:
      type: object
      required: [service, api_version, build, commit]
      properties:
        service: { const: silicon-iam }
        api_version: { const: v1 }
        build: { type: string }
        commit: { type: string }
    ApiVersionNegotiation:
      type: object
      required: [service, selected_api_version, supported_api_versions, build, commit]
      additionalProperties: false
      properties:
        service: { const: silicon-iam }
        selected_api_version: { type: string, pattern: '^v[1-9][0-9]{0,8}$' }
        supported_api_versions:
          type: array
          minItems: 1
          maxItems: 16
          uniqueItems: true
          description: Server-supported API majors in descending preference order.
          items: { type: string, pattern: '^v[1-9][0-9]{0,8}$' }
        build: { type: string }
        commit: { type: string }
    VerificationCode:
      type: string
      pattern: '^[0-9]{6}$'
      description: Six-digit code with a ten-minute expiry; ten failed verifications trigger a 60-second reusable-challenge cooldown.
    CarbonId:
      type: string
      pattern: '^[a-z1-9_-]{3,30}$'
      description: New immutable Carbon ID; zero is not admitted.
    ExistingCarbonId:
      type: string
      pattern: '^[a-z0-9_-]{3,30}$'
      description: Existing Carbon lookup/projection, including immutable legacy IDs containing zero.
    OrgId: { type: string, pattern: '^[a-z0-9_-]{3,50}$' }
    ApplicationHandle:
      type: string
      minLength: 1
      maxLength: 80
      pattern: '^[a-z][a-z0-9_-]{0,79}$'
      description: Local handle of 1-80 lowercase ASCII characters, starting with a letter and containing only letters, digits, underscores or hyphens. Supplied at creation; the public id becomes `{org_id}>{handle}`.
    AppId:
      type: string
      pattern: '^[a-z0-9_-]{3,50}>[a-z][a-z0-9_-]{0,79}$'
      description: Canonical organization-qualified Application id, `{org_id}>{handle}`; the local handle is 1-80 characters and the organization handle remains 3-50 characters.
    SiliconHandle:
      type: string
      pattern: '^[a-z0-9_-]{3,50}$'
      description: Client-supplied handle component; only the resulting `{handle}:{org_id}` Silicon ID is public.
    SiliconGlobalId: { type: string, pattern: '^[a-z0-9_-]{3,50}:[a-z0-9_-]{3,50}$' }
    TimeZoneId:
      type: string
      minLength: 1
      maxLength: 255
      pattern: '^[A-Za-z0-9._+-]+(/[A-Za-z0-9._+-]+)*$'
      description: Exact identifier that must resolve in the IANA Time Zone Database, such as UTC or Asia/Kolkata.
    EmailInput:
      type: object
      required: [email]
      additionalProperties: false
      properties: { email: { type: string, format: email, maxLength: 320 } }
    PhoneInput:
      type: object
      required: [phone_number]
      additionalProperties: false
      properties: { phone_number: { type: string, pattern: '^\\+[1-9][0-9]{7,14}$' } }
    AuthSession:
      type: object
      required: [session_id, expires_at]
      additionalProperties: false
      properties:
        session_id: { type: string, format: uuid }
        expires_at: { type: string, format: date-time }
        local_otp:
          type: string
          pattern: '^[0-9]{6}$'
          description: Present only when the explicitly configured local-development provider exposes the generated code.
    CodeDispatchResult:
      type: object
      required: [already_exists]
      additionalProperties: false
      properties:
        already_exists: { type: boolean }
        expires_in:
          type: integer
          const: 600
          description: Present only when a new verification code was sent.
        local_otp:
          type: string
          pattern: '^[0-9]{6}$'
          description: Present only when the explicitly configured local-development provider exposes the generated code.
    Availability:
      type: object
      required: [available]
      properties: { available: { type: boolean } }
    ActorRef:
      type: object
      required: [principal_id, type, public_id]
      additionalProperties: false
      properties:
        principal_id: { type: string, format: uuid }
        type: { type: string, enum: [carbon, silicon, application] }
        public_id: { type: string }
    CarbonPublic:
      type: object
      required: [principal_id, carbon_id, display_name, profile_photo, created_at]
      properties:
        principal_id: { type: string, format: uuid }
        carbon_id: { $ref: '#/components/schemas/ExistingCarbonId' }
        display_name: { type: string, minLength: 1, maxLength: 200 }
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: string, format: uri, maxLength: 2048 }
        created_at: { type: string, format: date-time }
    CarbonSuggestion:
      type: object
      required: [carbon_id]
      additionalProperties: false
      properties:
        carbon_id: { $ref: '#/components/schemas/ExistingCarbonId' }
    CarbonResolution:
      type: object
      required: [carbon_id]
      additionalProperties: false
      properties:
        carbon_id: { $ref: '#/components/schemas/ExistingCarbonId' }
    CarbonSelf:
      allOf:
        - { $ref: '#/components/schemas/CarbonPublic' }
        - type: object
          required: [timezone, email, phone_number, status, version, updated_at]
          properties:
            timezone: { $ref: '#/components/schemas/TimeZoneId' }
            email: { type: string, format: email }
            phone_number: { type: string }
            status: { type: string, enum: [active, suspended] }
            version: { type: integer, minimum: 1 }
            updated_at: { type: string, format: date-time }
    CarbonSignupComplete:
      type: object
      required: [carbon_id, display_name]
      additionalProperties: false
      properties:
        carbon_id: { $ref: '#/components/schemas/CarbonId' }
        display_name: { type: string, minLength: 1, maxLength: 200 }
        timezone:
          allOf: [{ $ref: '#/components/schemas/TimeZoneId' }]
          description: Defaults to UTC when omitted.
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: [string, 'null'], format: uri, maxLength: 2048 }
    CarbonProfilePatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        display_name: { type: string, minLength: 1, maxLength: 200 }
        timezone: { $ref: '#/components/schemas/TimeZoneId' }
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: [string, 'null'], format: uri, maxLength: 2048 }
    LoginChallengeCreate:
      type: object
      minProperties: 1
      maxProperties: 1
      oneOf:
        - { required: [email] }
        - { required: [phone_number] }
        - { required: [carbon_id] }
      additionalProperties: false
      properties:
        email: { type: string, format: email, maxLength: 320 }
        phone_number: { type: string, pattern: '^\\+[1-9][0-9]{7,14}$' }
        carbon_id: { $ref: '#/components/schemas/ExistingCarbonId' }
    StepUpAction:
      type: string
      description: >-
        Closed privileged-action catalog. account.session_revoke binds
        resource_id to the target session UUID; account.sessions_revoke_all
        binds it to the current Carbon principal UUID. The Silicon-webhook
        redirect action binds it to the target Silicon membership UUID. The
        Application client-secret rotation, webhook-secret rotation and
        webhook approval actions bind it to the internal Application UUID.
        Every action requires one non-null resource_id.
      enum:
        - account.session_revoke
        - account.sessions_revoke_all
        - organization.transfer_ownership
        - organization.authorization_change
        - organization.sso_change
        - organization.silicon_webhook.redirect
        - application.client_secret.rotate
        - application.webhook_secret.rotate
        - application.webhook.approve
        - silicon.rotate_token
        - platform_admin.sso_entitlement
        - platform_admin.application_review
    StepUpChallengeCreate:
      type: object
      required: [channel, action, resource_id]
      additionalProperties: false
      properties:
        channel: { type: string, enum: [email, phone_number] }
        action: { $ref: '#/components/schemas/StepUpAction' }
        resource_id: { type: string, format: uuid }
    StepUpTokenResponse:
      type: object
      required: [step_up_token, action, assurance, expires_in]
      properties:
        step_up_token: { type: string, pattern: '^sup_[A-Za-z0-9_-]{43}$' }
        action: { $ref: '#/components/schemas/StepUpAction' }
        assurance: { type: string, enum: [verified_channel] }
        expires_in: { type: integer, const: 300 }
    IamTokenResponse:
      type: object
      required: [access_token, refresh_token, token_type, expires_in, refresh_expires_at, actor, session_id]
      properties:
        access_token:
          type: string
          pattern: '^(cat|sat)_[A-Za-z0-9_-]{43}$'
          description: Carbon access tokens use cat_; Silicon access tokens use sat_.
        refresh_token: { type: string, pattern: '^rft_[A-Za-z0-9_-]{43}$' }
        token_type: { const: Bearer }
        expires_in: { type: integer, const: 1800 }
        refresh_expires_at: { type: string, format: date-time, description: Exactly 900 days from family creation. }
        actor: { $ref: '#/components/schemas/ActorRef' }
        session_id: { type: string, format: uuid }
    RefreshTokenRequest:
      type: object
      required: [refresh_token]
      additionalProperties: false
      properties:
        refresh_token: { type: string, minLength: 32, maxLength: 512 }
    TokenIntrospectionRequest:
      type: object
      required: [token]
      properties:
        token: { type: string, minLength: 32, maxLength: 4096 }
        token_type_hint: { type: string, enum: [access_token, refresh_token] }
    TokenIntrospection:
      type: object
      description: >-
        Live token state. An active access token also returns authorization, a
        synchronous bootstrap/resynchronization snapshot. No directory mutation
        or webhook delivery is required. An organization-bound token returns the
        one organization it is bound to in authorization. An unscoped token
        reaches only explicitly selected organizations with active membership: send
        X-Org-ID to select one of them and read authorization, or send no header
        and read authorizations, which lists one snapshot per organization and
        is empty when the subject holds no membership anywhere. Exactly one of
        the two fields is ever present. Refresh tokens carry no organization
        authorization.
      required: [active]
      properties:
        active: { type: boolean }
        principal_id: { type: string, format: uuid }
        actor_type: { type: string, enum: [carbon, silicon, application] }
        client_id: { $ref: '#/components/schemas/AppId' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        membership_id: { type: string, format: uuid }
        session_id: { type: string, format: uuid }
        scope: { type: string }
        audience: { type: string }
        issued_at: { type: integer }
        expires_at: { type: integer }
        authorization_epoch: { type: integer, minimum: 0 }
        authorization: { $ref: '#/components/schemas/ApplicationAuthorization' }
        authorizations:
          type: array
          items: { $ref: '#/components/schemas/ApplicationAuthorization' }
    AuthorizationTag:
      type: object
      required: [id, name]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
    ApplicationAuthorization:
      type: object
      description: >-
        Current active membership bound to the authenticated token or verified
        proof, audience, organization, principal and testing plane. Use on first
        login and cache misses; webhook snapshots are asynchronous updates, not
        prerequisites for initial access. org_role requires roles.read and tags
        requires memberships.read. Null means undisclosed, not member or empty
        tags. OBO disclosure uses the intersection of the parent token scopes
        and recipient application's currently approved scopes. The binding
        authorizes no action outside the verified proof's endpoint/request.
        Do not reuse it for a different principal, membership, epoch, audience,
        organization, environment or effective scope set. Never fill undisclosed
        fields from a broader cached token. Introspect current bearer tokens again
        before relying on cached authority; a consumed OBO proof is single-use.
      required: [principal_id, organization_id, org_id, membership_id, membership_version, authorization_epoch, audience, testing_environment_id, scopes, org_role, tags]
      properties:
        principal_id: { type: string, format: uuid }
        actor_type: { type: string, enum: [carbon, silicon] }
        public_id: { type: string }
        organization_id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        membership_id: { type: string, format: uuid }
        membership_version: { type: integer, minimum: 1 }
        authorization_epoch: { type: integer, minimum: 0 }
        audience: { $ref: '#/components/schemas/AppId' }
        testing_environment_id: { type: [string, 'null'], format: uuid }
        scopes: { type: array, items: { type: string } }
        org_role: { type: [string, 'null'], enum: [owner, admin, member, null] }
        tags: { type: [array, 'null'], items: { $ref: '#/components/schemas/AuthorizationTag' } }
    LogoutRequest:
      type: object
      additionalProperties: false
      properties:
        mode: { type: string, enum: [current_session, all_sessions], default: current_session }
    Session:
      type: object
      required: [session_id, actor, status, created_at, last_used_at, absolute_expires_at]
      properties:
        session_id: { type: string, format: uuid }
        actor: { $ref: '#/components/schemas/ActorRef' }
        status: { type: string, enum: [active, revoked, expired, replay_revoked] }
        user_agent_summary: { type: [string, 'null'], maxLength: 500 }
        ip_prefix: { type: [string, 'null'], maxLength: 64 }
        created_at: { type: string, format: date-time }
        last_used_at: { type: string, format: date-time }
        absolute_expires_at: { type: string, format: date-time }
        revoked_at: { type: [string, 'null'], format: date-time }
    PageInfo:
      type: object
      required: [next_cursor, has_more]
      properties:
        next_cursor: { type: [string, 'null'] }
        has_more: { type: boolean }
    SessionPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Session' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    LoginEvent:
      type: object
      required: [id, actor, event_type, occurred_at, success, request_id]
      properties:
        id: { type: string, format: uuid }
        actor: { $ref: '#/components/schemas/ActorRef' }
        app_id: { type: [string, 'null'] }
        org_id: { type: [string, 'null'] }
        event_type: { type: string, enum: [login_challenge, login_success, login_failure, oauth_authorization, oauth_token_exchange, logout, refresh_replay] }
        success: { type: boolean }
        ip_prefix: { type: [string, 'null'] }
        user_agent_summary: { type: [string, 'null'] }
        request_id: { type: string }
        occurred_at: { type: string, format: date-time }
    LoginEventPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/LoginEvent' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    SiliconAuthenticationRequest:
      type: object
      required: [silicon_id, silicon_token]
      additionalProperties: false
      properties:
        silicon_id: { $ref: '#/components/schemas/SiliconGlobalId' }
        silicon_token: { type: string, pattern: '^stk-[a-f0-9]{32}$' }
    BatchLoginSelection:
      type: object
      additionalProperties: false
      required: [app_id, org_ids, scope_version, approved_scopes]
      properties:
        scope_version: { type: integer, minimum: 1, description: Version of the consent view the user reviewed. }
        approved_scopes: { type: array, uniqueItems: true, items: { type: string }, description: "Explicitly approved full effective scope set; external entries use obo:{app_id}:{endpoint_id}." }
        app_id: { $ref: '#/components/schemas/AppId' }
        org_ids:
          type: array
          minItems: 1
          maxItems: 1000
          uniqueItems: true
          items: { $ref: '#/components/schemas/OrgId' }
          description: Explicit user selection for this app; existing grants are preserved.
    BatchLoginRequest:
      type: object
      additionalProperties: false
      required: [applications]
      properties:
        applications:
          type: array
          minItems: 1
          maxItems: 100
          items: { $ref: '#/components/schemas/BatchLoginSelection' }
          description: Unique app IDs, each with its own organization selection.
        redirect_uri: { type: string, format: uri, maxLength: 2048 }
    BatchLoginOrganizations:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/LoginOrganizations' }
    BatchLoginToken:
      type: object
      required: [app_id, slt, expires_in, expires_at]
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        slt: { type: string }
        expires_in: { type: integer }
        expires_at: { type: string, format: date-time, description: Absolute token expiry; unchanged by an idempotent replay. }
        request_id: { type: string, format: uuid }
    BatchLoginTokens:
      type: object
      required: [items]
      properties:
        items:
          type: array
          minItems: 1
          maxItems: 100
          items: { $ref: '#/components/schemas/BatchLoginToken' }
    ShortLivedTokenRequest:
      type: object
      required: [app_id, org_ids, scope_version, approved_scopes]
      additionalProperties: false
      properties:
        scope_version: { type: integer, minimum: 1, description: Version of the consent view the user reviewed. }
        approved_scopes: { type: array, uniqueItems: true, items: { type: string }, description: "Explicitly approved full effective scope set; external entries use obo:{app_id}:{endpoint_id}." }
        app_id: { $ref: '#/components/schemas/AppId' }
        org_ids:
          type: array
          minItems: 1
          maxItems: 1000
          uniqueItems: true
          items: { $ref: '#/components/schemas/OrgId' }
          description: User-selected active organizations. Adds to this application's existing grants on the same parent IAM session; never includes future memberships automatically. Only a direct IAM bearer may submit this selection.
        redirect_uri: { type: string, format: uri, maxLength: 2048 }
    LoginOrganization:
      type: object
      required: [org_id, name, authorized]
      properties:
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string }
        authorized: { type: boolean }
    LoginOrganizations:
      type: object
      required: [app_id, items, scope_version, consent_required, scopes]
      properties:
        scope_version: { type: integer, minimum: 1 }
        consent_required: { type: boolean }
        scopes: { type: array, items: { $ref: '#/components/schemas/ApplicationConsentScope' } }
        app_id: { $ref: '#/components/schemas/AppId' }
        app_name: { type: string }
        items:
          type: array
          items: { $ref: '#/components/schemas/LoginOrganization' }
    ShortLivedToken:
      type: object
      required: [slt, expires_in]
      properties:
        slt: { type: string }
        expires_in: { type: integer }
        request_id: { type: string, format: uuid, description: IAM login status identifier; never an application credential. }
    ApplicationTokenRequest:
      type: object
      required: [app_id]
      additionalProperties: false
      oneOf:
        - required: [slt]
        - required: [refresh_token]
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        slt: { type: string, minLength: 32, maxLength: 512 }
        refresh_token: { type: string, minLength: 32, maxLength: 512 }
    OAuthTokenResponse:
      type: object
      required: [access_token, refresh_token, token_type, expires_in, scope]
      properties:
        access_token: { type: string, pattern: '^oat_[A-Za-z0-9_-]{43}$' }
        refresh_token: { type: string, pattern: '^ort_[A-Za-z0-9_-]{43}$' }
        token_type: { const: Bearer }
        expires_in: { type: integer, const: 1800 }
        scope: { type: string }
        actor: { $ref: '#/components/schemas/ActorRef' }
        org_id: { type: [string, 'null'] }
    OAuthRevocationRequest:
      type: object
      required: [token]
      properties:
        token: { type: string, minLength: 32, maxLength: 4096 }
        token_type_hint: { type: string, enum: [access_token, refresh_token] }
    OrganizationCreate:
      type: object
      required: [org_id, name]
      additionalProperties: false
      properties:
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string, minLength: 1, maxLength: 200 }
        logo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        description: { type: [string, 'null'], maxLength: 5000 }
    OrganizationPatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 200 }
        logo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        description: { type: [string, 'null'], maxLength: 5000 }
        join_method: { type: string, enum: [email, sso] }
    Organization:
      type: object
      required: [id, org_id, name, owner_membership_id, join_method, sso_status, status, version, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string }
        logo: { type: [string, 'null'], format: uri }
        description: { type: [string, 'null'] }
        owner_membership_id: { type: string, format: uuid }
        join_method: { type: string, enum: [email, sso] }
        sso_status: { type: string, enum: [disabled, pending, active, error] }
        status: { type: string, enum: [active, disabled] }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    OrganizationPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Organization' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    OwnershipTransfer:
      type: object
      required: [new_owner_membership_id]
      additionalProperties: false
      properties:
        new_owner_membership_id: { type: string, format: uuid }
    OrganizationCapability:
      type: string
      enum:
        - organization.update
        - members.invite
        - members.update_directory
        - members.remove
        - silicons.create
        - silicons.update_directory
        - silicons.manage_hierarchy
        - silicons.remove
        - silicons.rotate_token
        - tags.manage
        - trust.manage
        - roles.request
        - roles.approve
        - admins.create
        - admins.manage
        - sso.manage
    Membership:
      type: object
      required: [id, org_id, principal, status, org_role, job_role, tags, extra_silicons, authorization_epoch, version, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        principal: { $ref: '#/components/schemas/ActorRef' }
        status: { type: string, enum: [active, removed] }
        org_role: { type: string, enum: [owner, admin, member] }
        job_role: { type: string, maxLength: 5000 }
        tags: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/TagSummary' } }
        first_silicon_membership_id: { type: [string, 'null'], format: uuid }
        extra_silicons: { type: array, uniqueItems: true, items: { type: string, format: uuid } }
        default_trust:
          description: Carbon-wide advisory trust baseline; null for Silicon memberships.
          oneOf:
            - { $ref: '#/components/schemas/TrustValue' }
            - { type: 'null' }
        reports_to_membership_id: { type: [string, 'null'], format: uuid }
        hierarchy_level: { type: [integer, 'null'], minimum: 1 }
        authorization_epoch: { type: integer, minimum: 0 }
        removed_at: { type: [string, 'null'], format: date-time }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    MembershipPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Membership' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    MembershipDirectoryPatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        first_silicon_membership_id: { type: [string, 'null'], format: uuid }
        extra_silicon_membership_ids: { type: array, uniqueItems: true, maxItems: 500, items: { type: string, format: uuid } }
        default_trust:
          allOf: [{ $ref: '#/components/schemas/TrustValue' }]
          description: Carbon-only advisory trust baseline; requires trust.manage.
        reports_to_membership_id: { type: [string, 'null'], format: uuid }
        profile_photo: { type: [string, 'null'], format: uri, maxLength: 2048 }
    MembershipAuthorization:
      type: object
      required: [membership_id, org_role, capabilities, authorization_epoch, version]
      properties:
        membership_id: { type: string, format: uuid }
        org_role: { type: string, enum: [owner, admin, member] }
        capabilities: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/OrganizationCapability' } }
        authorization_epoch: { type: integer, minimum: 0 }
        version: { type: integer, minimum: 1 }
    OrganizationCapabilitiesReplace:
      type: object
      required: [capabilities]
      additionalProperties: false
      properties:
        capabilities: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/OrganizationCapability' } }
    DirectoryRole:
      type: object
      required: [org_role, job_role]
      additionalProperties: false
      properties:
        org_role: { type: string, enum: [owner, admin, member] }
        job_role: { type: string, maxLength: 5000 }
    DirectoryOrganization:
      type: object
      required: [id, name]
      additionalProperties: false
      properties:
        id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string, minLength: 1, maxLength: 200 }
    DirectoryMember:
      type: object
      additionalProperties: false
      description: >-
        Sparse organization-directory projection. Omitted fields were not
        requested. trust is null when no Carbon-to-Silicon or
        Silicon-to-Carbon/Silicon orientation exists, including Carbon-to-Carbon.
      properties:
        name: { type: string, minLength: 1, maxLength: 200 }
        id:
          type: string
          description: Public Carbon ID or global Silicon ID.
        role: { $ref: '#/components/schemas/DirectoryRole' }
        org: { $ref: '#/components/schemas/DirectoryOrganization' }
        tags: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/TagSummary' } }
        trust:
          oneOf:
            - { $ref: '#/components/schemas/TrustEvaluation' }
            - { type: 'null' }
    DirectoryPage:
      type: object
      required: [items, page]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/DirectoryMember' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    TrustValue:
      type: object
      required: [boundary, level]
      additionalProperties: false
      properties:
        boundary: { type: string, enum: [internal, external] }
        level: { type: string, enum: [not_trusted, needs_approval, trusted] }
    InvitationTagTrustOverride:
      type: object
      required: [tag_id, trust]
      additionalProperties: false
      properties:
        tag_id: { type: string, format: uuid }
        trust: { $ref: '#/components/schemas/TrustValue' }
    InvitationSiliconTrustOverride:
      type: object
      required: [silicon_membership_id, trust]
      additionalProperties: false
      properties:
        silicon_membership_id: { type: string, format: uuid }
        trust: { $ref: '#/components/schemas/TrustValue' }
    CarbonInviteCreate:
      type: object
      required: [job_role, default_trust]
      minProperties: 3
      oneOf:
        - { required: [carbon_id] }
        - { required: [email] }
      additionalProperties: false
      properties:
        carbon_id: { $ref: '#/components/schemas/ExistingCarbonId' }
        email: { type: string, format: email, maxLength: 320 }
        job_role: { type: string, maxLength: 5000 }
        tag_ids: { type: array, uniqueItems: true, maxItems: 100, items: { type: string, format: uuid } }
        first_silicon_membership_id: { type: [string, 'null'], format: uuid }
        extra_silicon_membership_ids: { type: array, uniqueItems: true, maxItems: 500, items: { type: string, format: uuid } }
        default_trust: { $ref: '#/components/schemas/TrustValue' }
        tag_trust_overrides:
          type: array
          maxItems: 100
          default: []
          description: At most one override per active organization tag.
          items: { $ref: '#/components/schemas/InvitationTagTrustOverride' }
        silicon_trust_overrides:
          type: array
          maxItems: 500
          default: []
          description: At most one override per active Silicon membership.
          items: { $ref: '#/components/schemas/InvitationSiliconTrustOverride' }
        redirect_app_id:
          type: [string, 'null']
          pattern: '^[a-z0-9_-]{3,50}>[a-z][a-z0-9_-]{0,79}$'
          description: Canonical organization-qualified Application id when the invitation should continue into an Application login.
    InviteStatus: { type: string, enum: [pending, accepted, revoked, expired] }
    Invite:
      type: object
      required: [id, org_id, org_role, job_role, tag_ids, extra_silicon_membership_ids, default_trust, tag_trust_overrides, silicon_trust_overrides, invited_by, status, expires_at, version, created_at]
      properties:
        id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        target_carbon:
          $ref: '#/components/schemas/CarbonPublic'
          description: Present after the invitation is bound to a Carbon account; absent before signup and verified email binding.
        masked_delivery_address: { type: string }
        org_role: { const: member }
        job_role: { type: string, maxLength: 5000 }
        tag_ids: { type: array, uniqueItems: true, items: { type: string, format: uuid } }
        first_silicon_membership_id: { type: [string, 'null'], format: uuid }
        extra_silicon_membership_ids: { type: array, uniqueItems: true, items: { type: string, format: uuid } }
        default_trust: { $ref: '#/components/schemas/TrustValue' }
        tag_trust_overrides:
          type: array
          items: { $ref: '#/components/schemas/InvitationTagTrustOverride' }
        silicon_trust_overrides:
          type: array
          items: { $ref: '#/components/schemas/InvitationSiliconTrustOverride' }
        invited_by: { $ref: '#/components/schemas/ActorRef' }
        status: { $ref: '#/components/schemas/InviteStatus' }
        expires_at: { type: string, format: date-time }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        accepted_at: { type: [string, 'null'], format: date-time }
    InvitePage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Invite' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    InvitationAcceptance:
      type: object
      required: [invite_id, verification_code]
      additionalProperties: false
      properties:
        invite_id: { type: string, format: uuid }
        verification_code: { $ref: '#/components/schemas/VerificationCode' }
    InvitationEmailCodeResponse:
      type: object
      required: [accepted, invite_id, expires_in]
      additionalProperties: false
      properties:
        accepted: { const: true }
        invite_id: { type: string, format: uuid }
        expires_in: { type: integer, minimum: 1, maximum: 600 }
    SiliconCreate:
      type: object
      required: [silicon_id, job_role]
      additionalProperties: false
      properties:
        silicon_id: { $ref: '#/components/schemas/SiliconHandle' }
        display_name:
          type: string
          minLength: 1
          maxLength: 200
          description: Defaults to the immutable local Silicon handle when omitted.
        timezone:
          allOf: [{ $ref: '#/components/schemas/TimeZoneId' }]
          description: Defaults to UTC when omitted.
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        job_role: { type: string, maxLength: 5000 }
        reports_to_membership_id: { type: [string, 'null'], format: uuid }
        tag_ids: { type: array, uniqueItems: true, maxItems: 100, items: { type: string, format: uuid } }
    SiliconPatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        display_name: { type: string, minLength: 1, maxLength: 200 }
        timezone: { $ref: '#/components/schemas/TimeZoneId' }
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        reports_to_membership_id: { type: [string, 'null'], format: uuid }
    Silicon:
      type: object
      required: [principal_id, membership_id, silicon_id, org_id, display_name, timezone, profile_photo, job_role, tags, hierarchy_level, webhook_configured, status, version, created_at, updated_at]
      properties:
        principal_id: { type: string, format: uuid }
        membership_id: { type: string, format: uuid }
        silicon_id: { $ref: '#/components/schemas/SiliconGlobalId' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        display_name: { type: string, minLength: 1, maxLength: 200 }
        timezone: { $ref: '#/components/schemas/TimeZoneId' }
        description: { type: [string, 'null'], maxLength: 5000 }
        profile_photo: { type: string, format: uri }
        job_role: { type: string, maxLength: 5000 }
        reports_to_membership_id: { type: [string, 'null'], format: uuid }
        tags: { type: array, items: { $ref: '#/components/schemas/TagSummary' } }
        hierarchy_level: { type: integer, minimum: 1 }
        webhook_configured: { type: boolean }
        status: { type: string, enum: [active, removed] }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    SiliconCreated:
      type: object
      required: [silicon, silicon_token, secret_replay_expires_at]
      properties:
        silicon: { $ref: '#/components/schemas/Silicon' }
        silicon_token: { type: string, pattern: '^stk-[a-f0-9]{32}$' }
        secret_replay_expires_at: { type: string, format: date-time }
    SiliconPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Silicon' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    SiliconWebhookReplace:
      type: object
      required: [url]
      additionalProperties: false
      properties:
        url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
    SiliconWebhook:
      type: object
      required: [silicon_id, url, status, secret_version, version, created_at, updated_at]
      properties:
        silicon_id: { $ref: '#/components/schemas/SiliconGlobalId' }
        url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        status: { const: active }
        secret_version: { type: integer, minimum: 1 }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    SiliconWebhookConfigured:
      type: object
      required: [webhook, webhook_signing_secret, secret_replay_expires_at]
      properties:
        webhook: { $ref: '#/components/schemas/SiliconWebhook' }
        webhook_signing_secret: { type: string, pattern: '^swhs_[A-Za-z0-9_-]{43}$' }
        secret_replay_expires_at: { type: string, format: date-time }
    SiliconWebhookSubscriptionTopic:
      type: string
      enum: [membership_lifecycle, member_updates, trust_updates]
    SiliconWebhookSubscriptionReplace:
      type: object
      required: [mode]
      additionalProperties: false
      description: >-
        all receives every explicitly Silicon-routed organization event, including
        Full-only metadata, catalog, invitation, governance, credential, and
        configuration events, and canonicalizes the response to all three topic
        values. selected requires at least one exact topic: membership_lifecycle is
        actual creation/reactivation/removal, member_updates is applied existing-member
        role/tag/profile/hierarchy/authorization/ownership change, and trust_updates is
        trust state only. Optional tag_filter always includes the Silicon's own
        event-time before/after tag audience and may add active organization tags;
        later own-tag changes never alter an older event's audience.
      properties:
        mode: { type: string, enum: [all, selected] }
        topics:
          type: array
          uniqueItems: true
          maxItems: 3
          default: []
          items: { $ref: '#/components/schemas/SiliconWebhookSubscriptionTopic' }
        tag_filter:
          oneOf:
            - { $ref: '#/components/schemas/SiliconWebhookTagFilter' }
            - { type: 'null' }
    SiliconWebhookTagFilter:
      type: object
      additionalProperties: false
      properties:
        additional_tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          default: []
          items: { type: string, format: uuid }
    SiliconWebhookSubscription:
      type: object
      required: [silicon_id, mode, topics, tag_filter, version, created_at, updated_at]
      properties:
        silicon_id: { $ref: '#/components/schemas/SiliconGlobalId' }
        mode: { type: string, enum: [all, selected] }
        topics:
          type: array
          minItems: 1
          maxItems: 3
          uniqueItems: true
          items: { $ref: '#/components/schemas/SiliconWebhookSubscriptionTopic' }
        tag_filter:
          description: >-
            Null disables tag filtering. When present, organization-wide and
            unattributed events are suppressed; affected tags must intersect
            either the Silicon's immutable event-time own-tag audience or a
            currently configured additional tag.
          oneOf:
            - { $ref: '#/components/schemas/SiliconWebhookTagFilter' }
            - { type: 'null' }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    SiliconTokenRotated:
      type: object
      required: [silicon_id, credential_version, silicon_token, secret_replay_expires_at]
      properties:
        silicon_id: { $ref: '#/components/schemas/SiliconGlobalId' }
        credential_version: { type: integer, minimum: 1 }
        silicon_token: { type: string, pattern: '^stk-[a-f0-9]{32}$' }
        secret_replay_expires_at: { type: string, format: date-time }
    TagCreate:
      type: object
      required: [name]
      additionalProperties: false
      properties: { name: { type: string, minLength: 1, maxLength: 100 } }
    TagPatch:
      type: object
      required: [name]
      additionalProperties: false
      properties: { name: { type: string, minLength: 1, maxLength: 100 } }
    TagSummary:
      type: object
      required: [id, name]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
    Tag:
      allOf:
        - { $ref: '#/components/schemas/TagSummary' }
        - type: object
          required: [org_id, version, created_at, updated_at]
          properties:
            org_id: { $ref: '#/components/schemas/OrgId' }
            version: { type: integer, minimum: 1 }
            created_at: { type: string, format: date-time }
            updated_at: { type: string, format: date-time }
    TagPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Tag' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    TestingEnvironment:
      type: object
      required:
        [id, org_id, name, description, status, created_by_membership_id,
         key_generation, key_rotated_at, last_activity_at, cleaned_at,
         deleted_at, purge_after, version, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string, minLength: 1, maxLength: 64 }
        description: { type: [string, 'null'], maxLength: 500 }
        status: { type: string, enum: [active, deleted] }
        created_by_membership_id:
          type: string
          format: uuid
          description: Membership that created the environment; it keeps administrative authority while active.
        key_generation:
          type: integer
          minimum: 1
          description: Increments on every key rotation.
        key_rotated_at: { type: [string, 'null'], format: date-time }
        last_activity_at:
          type: string
          format: date-time
          description: Last accepted request in the environment; idleness beyond the configured window auto-deletes it.
        cleaned_at: { type: [string, 'null'], format: date-time }
        deleted_at: { type: [string, 'null'], format: date-time }
        purge_after:
          type: [string, 'null']
          format: date-time
          description: Deadline after which the environment and its data are destroyed permanently. Restorable until then.
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    TestingEnvironmentPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/TestingEnvironment' } }
        page:
          type: object
          required: [next_cursor, has_more]
          properties:
            next_cursor: { type: [string, 'null'], format: uuid }
            has_more: { type: boolean }
    TestingEnvironmentCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name: { type: string, minLength: 1, maxLength: 64 }
        description: { type: [string, 'null'], maxLength: 500 }
    TestingEnvironmentPatch:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: { type: string, minLength: 1, maxLength: 64 }
        description: { type: [string, 'null'], maxLength: 500 }
    TestingEnvironmentKeyValue:
      type: string
      pattern: '^[A-Za-z0-9]{32}$'
      description: Root authority for one environment. Anyone holding it can do anything inside that environment.
    TestingEnvironmentWithKey:
      allOf:
        - { $ref: '#/components/schemas/TestingEnvironment' }
        - type: object
          required: [key]
          properties:
            key: { $ref: '#/components/schemas/TestingEnvironmentKeyValue' }
    TestingEnvironmentKey:
      type: object
      required: [environment_id, key_generation, key_rotated_at, key]
      properties:
        environment_id: { type: string, format: uuid }
        key_generation: { type: integer, minimum: 1 }
        key_rotated_at: { type: [string, 'null'], format: date-time }
        key: { $ref: '#/components/schemas/TestingEnvironmentKeyValue' }
    TestingEnvironmentSelf:
      type: object
      required: [id, name, description, key_generation, created_at]
      description: What a key holder may see about the environment it holds a key to.
      properties:
        id: { type: string, format: uuid }
        name: { type: string, minLength: 1, maxLength: 64 }
        description: { type: [string, 'null'], maxLength: 500 }
        key_generation: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
    TestingEnvironmentCleaning:
      type: object
      required: [environment_id, erased_rows, cleaned_at]
      properties:
        environment_id: { type: string, format: uuid }
        erased_rows: { type: integer, minimum: 0 }
        cleaned_at: { type: string, format: date-time }
    TestingApplicationImport:
      type: object
      required: [app_id]
      additionalProperties: false
      properties:
        app_id:
          $ref: '#/components/schemas/AppId'
          description: Canonical production Application id to copy.
    TestingApplicationImported:
      type: object
      required: [application, app_secret, app_secret_version, webhook_secret_inherited, secret_replay_expires_at]
      additionalProperties: false
      properties:
        application: { $ref: '#/components/schemas/Application' }
        app_secret:
          type: string
          pattern: '^ask_[A-Za-z0-9_-]{43}$'
          description: Fresh credential valid only inside this testing environment.
        app_secret_version: { type: integer, minimum: 1 }
        webhook_secret_inherited:
          type: boolean
          const: true
          description: Confirms that the production signing secret was inherited but not disclosed.
        secret_replay_expires_at: { type: string, format: date-time }
    TrustSelector:
      type: object
      additionalProperties: false
      oneOf:
        - required: [kind, tag_id]
          properties: { kind: { const: tag }, tag_id: { type: string, format: uuid } }
        - required: [kind, membership_id]
          properties: { kind: { const: membership }, membership_id: { type: string, format: uuid } }
      properties:
        kind: { type: string, enum: [tag, membership] }
        tag_id: { type: string, format: uuid }
        membership_id: { type: string, format: uuid }
    TrustRuleCreate:
      type: object
      required: [subject, target, trust]
      additionalProperties: false
      properties:
        subject: { $ref: '#/components/schemas/TrustSelector' }
        target: { $ref: '#/components/schemas/TrustSelector' }
        trust: { $ref: '#/components/schemas/TrustValue' }
    TrustRulePatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        subject: { $ref: '#/components/schemas/TrustSelector' }
        target: { $ref: '#/components/schemas/TrustSelector' }
        trust: { $ref: '#/components/schemas/TrustValue' }
    TrustRule:
      allOf:
        - { $ref: '#/components/schemas/TrustRuleCreate' }
        - type: object
          required: [id, org_id, specificity, version, created_at, updated_at]
          properties:
            id: { type: string, format: uuid }
            org_id: { $ref: '#/components/schemas/OrgId' }
            specificity: { type: integer, minimum: 0, maximum: 2 }
            version: { type: integer, minimum: 1 }
            created_at: { type: string, format: date-time }
            updated_at: { type: string, format: date-time }
    TrustRulePage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/TrustRule' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    TrustEvaluationRequest:
      type: object
      required: [subject_membership_id, target_silicon_membership_id]
      additionalProperties: false
      properties:
        subject_membership_id: { type: string, format: uuid }
        target_silicon_membership_id: { type: string, format: uuid }
    TrustEvaluation:
      type: object
      required: [trust, source, matching_rule_ids, advisory]
      properties:
        trust: { $ref: '#/components/schemas/TrustValue' }
        source: { type: string, enum: [organization_default, tag_rule, exact_rule] }
        matching_rule_ids: { type: array, items: { type: string, format: uuid } }
        advisory: { const: true }
    ApprovalKind: { type: string, enum: [carbon_job_role_change, silicon_job_role_change, carbon_tag_change, silicon_tag_change, silicon_token_rotation] }
    ApprovalStatus: { type: string, enum: [pending, approved, rejected, completed] }
    RoleChangeRequestCreate:
      type: object
      required: [target_membership_id, proposed_job_role]
      additionalProperties: false
      properties:
        target_membership_id: { type: string, format: uuid }
        proposed_job_role: { type: string, maxLength: 5000 }
        reason: { type: [string, 'null'], maxLength: 2000 }
    TagChangeRequestCreate:
      type: object
      additionalProperties: false
      anyOf:
        - required: [add_tag_ids]
          properties:
            add_tag_ids: { minItems: 1 }
        - required: [remove_tag_ids]
          properties:
            remove_tag_ids: { minItems: 1 }
      properties:
        add_tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          default: []
          items: { type: string, format: uuid }
        remove_tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          default: []
          items: { type: string, format: uuid }
        reason: { type: [string, 'null'], minLength: 1, maxLength: 2000 }
    DirectJobRoleReplace:
      type: object
      required: [job_role]
      additionalProperties: false
      properties:
        job_role: { type: string, maxLength: 5000 }
    DirectTagSetReplace:
      type: object
      required: [tag_ids]
      additionalProperties: false
      properties:
        tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          items: { type: string, format: uuid }
    ApprovalDecisionCreate:
      type: object
      required: [decision]
      additionalProperties: false
      properties:
        decision: { type: string, enum: [approve, reject] }
        comment: { type: [string, 'null'], maxLength: 2000 }
    ApprovalDecision:
      type: object
      required: [id, approver, decision, decided_at]
      properties:
        id: { type: string, format: uuid }
        approver: { $ref: '#/components/schemas/ActorRef' }
        decision: { type: string, enum: [approve, reject] }
        comment: { type: [string, 'null'] }
        decided_at: { type: string, format: date-time }
    ApprovalRequest:
      type: object
      required: [id, org_id, kind, status, requested_by, target_membership_id, immutable_payload, required_approvals, decisions, version, created_at]
      properties:
        id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        kind: { $ref: '#/components/schemas/ApprovalKind' }
        status: { $ref: '#/components/schemas/ApprovalStatus' }
        requested_by: { $ref: '#/components/schemas/ActorRef' }
        target_membership_id: { type: string, format: uuid }
        immutable_payload: { type: object, additionalProperties: true }
        required_approvals:
          type: object
          required: [target_carbon, eligible_owner_or_admin]
          properties:
            target_carbon: { type: integer, minimum: 0, maximum: 1 }
            eligible_owner_or_admin: { type: integer, minimum: 1, maximum: 1 }
        decisions: { type: array, items: { $ref: '#/components/schemas/ApprovalDecision' } }
        completed_at: { type: [string, 'null'], format: date-time }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
    ApprovalRequestPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/ApprovalRequest' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    RoleHistory:
      type: object
      required: [id, membership_id, old_job_role, new_job_role, requested_by, approvers, approval_request_id, applied_at]
      properties:
        id: { type: string, format: uuid }
        membership_id: { type: string, format: uuid }
        old_job_role: { type: string }
        new_job_role: { type: string }
        requested_by: { $ref: '#/components/schemas/ActorRef' }
        approvers: { type: array, items: { $ref: '#/components/schemas/ActorRef' } }
        approval_request_id:
          type: [string, 'null']
          format: uuid
          description: Null for an owner/admin direct change.
        applied_at: { type: string, format: date-time }
    RoleHistoryPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/RoleHistory' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    TagHistory:
      type: object
      required: [id, membership_id, previous_tag_ids, applied_tag_ids, requested_by, approvers, approval_request_id, membership_version, applied_at]
      properties:
        id: { type: string, format: uuid }
        membership_id: { type: string, format: uuid }
        previous_tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          items: { type: string, format: uuid }
        applied_tag_ids:
          type: array
          uniqueItems: true
          maxItems: 100
          items: { type: string, format: uuid }
        requested_by: { $ref: '#/components/schemas/ActorRef' }
        approvers: { type: array, items: { $ref: '#/components/schemas/ActorRef' } }
        approval_request_id:
          type: [string, 'null']
          format: uuid
          description: Null for an owner/admin direct change.
        membership_version: { type: integer, minimum: 1 }
        applied_at: { type: string, format: date-time }
    TagHistoryPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/TagHistory' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    SsoEntitlement:
      type: object
      required: [enabled, version]
      properties:
        enabled: { type: boolean }
        reason: { type: [string, 'null'], maxLength: 2000 }
        version: { type: integer, minimum: 1 }
    SsoConfiguration:
      type: object
      required: [org_id, entitled, status, join_method, version, updated_at]
      properties:
        org_id: { $ref: '#/components/schemas/OrgId' }
        entitled: { type: boolean }
        status: { type: string, enum: [disabled, pending, active, error] }
        join_method: { type: string, enum: [email, sso] }
        workos_organization_id: { type: [string, 'null'] }
        connection_id: { type: [string, 'null'] }
        version: { type: integer, minimum: 1 }
        updated_at: { type: string, format: date-time }
    SsoSetupLink:
      type: object
      required: [url, expires_in, expires_at]
      properties:
        url: { type: string, format: uri }
        expires_in: { type: integer, const: 300 }
        expires_at: { type: string, format: date-time }
    TestResult:
      type: object
      required: [ok, checked_at]
      properties:
        ok: { type: boolean }
        message: { type: [string, 'null'], maxLength: 1000 }
        checked_at: { type: string, format: date-time }
    ApplicationStatus:
      type: string
      enum: [under_review, verified, rejected, suspended, deleted]
    ApplicationCreate:
      type: object
      required: [app_id, org_id, webhook_url, webhook_secret, base_url]
      additionalProperties: false
      description: Requires the authenticated Carbon to be a current active owner/admin of org_id.
      properties:
        app_scope: { $ref: '#/components/schemas/ApplicationScope' }
        webhook_scope: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/ApplicationWebhookScope' } }
        obo_review_message: { type: string, maxLength: 10000 }
        testing_idle_days: { type: integer, minimum: 1, maximum: 3650, default: 30 }
        app_id: { $ref: '#/components/schemas/ApplicationHandle' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        app_name: { type: [string, 'null'], maxLength: 200 }
        app_logo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        webhook_url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        webhook_secret: { $ref: '#/components/schemas/ApplicationWebhookSecret' }
        base_url:
          type: string
          format: uri
          maxLength: 2048
          description: >-
            Pathless Application backend origin with no trailing slash,
            credentials, query or fragment. HTTPS is required except for
            literal loopback HTTP in local development.
        obo_endpoints:
          type: array
          maxItems: 50
          default: []
          items: { $ref: '#/components/schemas/ApplicationOboEndpoint' }
    ApplicationPatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        app_scope: { $ref: '#/components/schemas/ApplicationScope' }
        webhook_scope: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/ApplicationWebhookScope' } }
        obo_review_message: { type: string, maxLength: 10000 }
        testing_idle_days: { type: integer, minimum: 1, maximum: 3650, default: 30 }
        app_name: { type: [string, 'null'], maxLength: 200 }
        app_logo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        base_url:
          type: string
          format: uri
          maxLength: 2048
          description: >-
            Pathless backend origin without a trailing slash; HTTPS except for
            literal loopback development.
        obo_endpoints:
          type: array
          maxItems: 50
          description: Full replacement. An empty array retires every active endpoint.
          items: { $ref: '#/components/schemas/ApplicationOboEndpoint' }
    Application:
      type: object
      required: [id, app_id, org_id, created_by, base_url, requested_scopes, approved_scopes, obo_endpoints, status, webhook, has_pending_changes, version, created_at, updated_at, app_scope, effective_app_scope, webhook_scope, scope_version, testing_idle_days]
      description: Organization-owned Application. created_by is immutable provenance and does not confer management authority.
      properties:
        app_scope: { $ref: '#/components/schemas/ApplicationScope' }
        webhook_scope: { type: array, uniqueItems: true, items: { $ref: '#/components/schemas/ApplicationWebhookScope' } }
        obo_review_message: { type: string, maxLength: 10000 }
        testing_idle_days: { type: integer, minimum: 1, maximum: 3650, default: 30 }
        effective_app_scope: { $ref: '#/components/schemas/ApplicationScope' }
        scope_version: { type: integer, minimum: 1 }
        id: { type: string, format: uuid }
        app_id: { $ref: '#/components/schemas/AppId' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        created_by: { $ref: '#/components/schemas/ActorRef' }
        app_name: { type: [string, 'null'] }
        app_logo: { type: [string, 'null'], format: uri }
        base_url:
          type: string
          format: uri
          maxLength: 2048
          description: Pathless backend origin without a trailing slash.
        requested_scopes: { type: array, uniqueItems: true, items: { type: string } }
        approved_scopes: { type: array, uniqueItems: true, items: { type: string } }
        obo_endpoints:
          type: array
          maxItems: 50
          items: { $ref: '#/components/schemas/ApplicationOboEndpoint' }
        status: { $ref: '#/components/schemas/ApplicationStatus' }
        webhook: { $ref: '#/components/schemas/ApplicationWebhook' }
        has_pending_changes: { type: boolean }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ApplicationCreated:
      type: object
      required: [application, app_secret, app_secret_version, webhook_signing_secret, webhook_secret_version, secret_replay_expires_at]
      properties:
        application: { $ref: '#/components/schemas/Application' }
        app_secret: { type: string, pattern: '^ask_[A-Za-z0-9_-]{43}$' }
        app_secret_version: { type: integer, minimum: 1 }
        webhook_signing_secret: { $ref: '#/components/schemas/ApplicationWebhookSecret' }
        webhook_secret_version: { type: integer, minimum: 1 }
        secret_replay_expires_at: { type: string, format: date-time }
    ApplicationSecretRotated:
      type: object
      required: [app_id, app_secret, app_secret_version, application_version, secret_replay_expires_at]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        app_secret: { type: string, pattern: '^ask_[A-Za-z0-9_-]{43}$' }
        app_secret_version: { type: integer, minimum: 1 }
        application_version: { type: integer, minimum: 1 }
        secret_replay_expires_at: { type: string, format: date-time }
    ApplicationWebhookSecretRotated:
      type: object
      required: [app_id, webhook_signing_secret, webhook_secret_version, application_version, secret_replay_expires_at]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        webhook_signing_secret: { $ref: '#/components/schemas/ApplicationWebhookSecret' }
        webhook_secret_version: { type: integer, minimum: 1 }
        application_version: { type: integer, minimum: 1 }
        secret_replay_expires_at: { type: string, format: date-time }
    ApplicationBaseUrl:
      type: object
      required: [app_id, base_url]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        base_url:
          type: string
          format: uri
          maxLength: 2048
          description: Pathless backend origin without a trailing slash.
    ApplicationPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Application' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    AdminApplication:
      allOf:
        - { $ref: '#/components/schemas/Application' }
    AdminApplicationPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/AdminApplication' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    ApplicationWebhook:
      type: object
      required: [active_url, status, secret_version, version]
      properties:
        application_id:
          type: [string, 'null']
          format: uuid
          description: Internal Application UUID for binding webhook-approval step-up. Returned on webhook configuration reads and mutations.
        active_url:
          type: [string, 'null']
          format: uri
          description: Null until the application's initial destination is approved; a verified application's pending endpoint may be approved by its current organization owner/admin or an IAM platform reviewer.
        pending_url: { type: [string, 'null'], format: uri }
        status: { type: string, enum: [pending_review, active, replacement_under_review, disabled] }
        secret_version: { type: integer, minimum: 1 }
        webhook_signing_secret:
          $ref: '#/components/schemas/ApplicationWebhookSecret'
          description: >-
            Echoes a caller-supplied replacement secret for v1 compatibility.
        secret_replay_expires_at:
          type: string
          format: date-time
          description: Present exactly when webhook_signing_secret is present.
        version:
          type: integer
          minimum: 1
          description: Application aggregate version; identical to the response ETag and the value required by If-Match for replacement or approval.
    ApplicationWebhookReplace:
      type: object
      required: [url]
      additionalProperties: false
      description: >-
        Replaces only the reviewed destination candidate. It normally reuses
        the existing encrypted signing secret in production. Testing URL replacements
        install a supplied test-only secret or generate one when omitted and return
        webhook_signing_secret with its replay expiry. Supplying a secret for any
        replacement installs it for the new endpoint.
      properties:
        url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        webhook_secret: { $ref: '#/components/schemas/ApplicationWebhookSecret' }
    ApplicationWebhookSecret:
      type: string
      minLength: 32
      maxLength: 512
      pattern: '^[!-~]+$'
      description: >-
        Caller-chosen Application webhook signing secret containing only
        non-whitespace ASCII characters. IAM encrypts it at rest. Testing webhook
        URL replacement may generate a fresh secret when one is not supplied.
    ApplicationWebhookSecretRotate:
      type: object
      required: [webhook_secret]
      additionalProperties: false
      properties:
        webhook_secret: { $ref: '#/components/schemas/ApplicationWebhookSecret' }
    WebhookDeadLetter:
      type: object
      required: [delivery_id, event_id, event_type, occurred_at, aggregate_type, aggregate_id, aggregate_version, status, attempt_count, cycle_attempt_count, manual_replay_count, last_http_status, last_error_code, dead_lettered_at, version]
      additionalProperties: false
      properties:
        delivery_id: { type: string, format: uuid }
        event_id: { type: string, format: uuid }
        event_type: { type: string }
        occurred_at: { type: string, format: date-time }
        aggregate_type: { type: string }
        aggregate_id: { type: string, format: uuid }
        aggregate_version: { type: integer, minimum: 1 }
        status: { type: string, enum: [pending, dead_letter] }
        attempt_count: { type: integer, minimum: 0 }
        cycle_attempt_count: { type: integer, minimum: 0 }
        manual_replay_count: { type: integer, minimum: 0 }
        last_http_status: { type: [integer, 'null'], minimum: 100, maximum: 599 }
        last_error_code: { type: [string, 'null'] }
        dead_lettered_at: { type: [string, 'null'], format: date-time }
        version: { type: integer, minimum: 1 }
    WebhookDeadLetterPage:
      type: object
      required: [items, page]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/WebhookDeadLetter' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    WebhookReplayRequest:
      type: object
      required: [delivery_ids]
      additionalProperties: false
      properties:
        delivery_ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items: { type: string, format: uuid }
    WebhookReplayResponse:
      type: object
      required: [deliveries, replayed_count]
      additionalProperties: false
      properties:
        deliveries:
          type: array
          maxItems: 100
          items: { $ref: '#/components/schemas/WebhookDeadLetter' }
        replayed_count: { type: integer, minimum: 1, maximum: 100 }
    WebhookEvent:
      type: object
      required: [spec_version, event_id, event_type, occurred_at, aggregate, data]
      properties:
        spec_version: { const: '1.0' }
        event_id: { type: string, format: uuid }
        event_type:
          type: string
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+\.v[1-9][0-9]*$'
          description: Stable dotted event name with its positive schema version suffix.
        occurred_at: { type: string, format: date-time }
        organization_id: { type: [string, 'null'], format: uuid }
        aggregate:
          type: object
          required: [id, type, version]
          properties:
            id: { type: string, format: uuid }
            type: { type: string }
            version: { type: integer, minimum: 1 }
        data:
          type: object
          additionalProperties: true
          description: >-
            Event-type-specific authorized projection. Application delivery requires effective declared
            app scopes, user consent, selected active memberships, and matching webhook event categories.
            Application recipient unions, payloads, changed_fields, and resource versions are captured
            in the domain transaction; workers do not hydrate historical events from later state.
            self.profile.read exposes only display_name, profile_photo, description, and timezone;
            identity, contacts, membership status, and other data have separate scopes.
            self.organizations.read exposes organization identifiers, name, logo, description, and
            resource version, never SSO or administrative/security configuration.
            Member events use current.members; organization updates use current.organization.
            Captured invitation, governance, and tag-creation snapshots use current.resource and require
            organization.invitations.read, organization.governance.read, and organization.tags.read,
            respectively. Tag/trust changes may include current.resource plus current.members.
            Raw trust configuration requires organization.trust.read. self.trust.read exposes only
            membership.effective_trust from the user's perspective, with target_silicon_membership_id,
            trust, and advisory, and no raw defaults, rules, overrides, or rule identifiers.
            Before-only recipients receive stable resource/version authorization tombstones, not
            removed private fields. Application payloads never include credentials or raw provider
            records, SSO events, or Silicon credential-management and webhook/subscription configuration.
            A completed Silicon credential rotation may expose only its permitted authorization-epoch
            and access projection. Silicon subscriptions retain their separate event vocabulary.
            Applications receive carbon.updated.v1 with scope-filtered fields;
            organization.membership.profile_updated.v1 is the organization-bound Silicon notification
            of that profile mutation, not a second Application event.
    TestingWebhookEvent:
      type: object
      required: [test]
      additionalProperties: false
      description: >-
        Explicit test-plane envelope. The signature covers these exact outer
        bytes. testing_key is the environment root credential and must never
        be logged or persisted with the event.
      properties:
        test:
          type: object
          required: [testing_key, metadata, data]
          additionalProperties: false
          properties:
            testing_key: { $ref: '#/components/schemas/TestingEnvironmentKeyValue' }
            metadata:
              type: object
              required: [spec_version, event_id, event_type, occurred_at, organization_id, aggregate]
              additionalProperties: false
              properties:
                spec_version: { const: '1.0' }
                event_id: { type: string, format: uuid }
                event_type:
                  type: string
                  pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+\.v[1-9][0-9]*$'
                occurred_at: { type: string, format: date-time }
                organization_id: { type: [string, 'null'], format: uuid }
                aggregate:
                  type: object
                  required: [type, id, version]
                  additionalProperties: false
                  properties:
                    type: { type: string }
                    id: { type: string, format: uuid }
                    version: { type: integer, minimum: 1 }
            data:
              type: object
              additionalProperties: true
    SiliconFullEventType:
      type: string
      description: Exact closed 38-event vocabulary delivered by Silicon mode=all subscriptions.
      enum:
        - organization.membership.created.v1
        - organization.membership.reactivated.v1
        - organization.membership.removed.v1
        - organization.silicon.created.v1
        - organization.silicon.removed.v1
        - organization.membership.updated.v1
        - organization.membership.profile_updated.v1
        - organization.membership.authorization_updated.v1
        - organization.ownership_transferred.v1
        - organization.admin.promoted.v1
        - organization.admin.demoted.v1
        - organization.silicon.updated.v1
        - organization.tag_updated.v1
        - organization.trust.default_updated.v1
        - organization.trust.rule_created.v1
        - organization.trust.rule_updated.v1
        - organization.trust.rule_archived.v1
        - organization.created.v1
        - organization.updated.v1
        - organization.tag_created.v1
        - organization.invitation.created.v1
        - organization.invitation.accepted.v1
        - organization.invitation.revoked.v1
        - organization.role_change.requested.v1
        - organization.tag_change.requested.v1
        - organization.approval.decided.v1
        - organization.silicon.rotation_requested.v1
        - organization.silicon.credential_rotated.v1
        - organization.silicon.webhook.configured.v1
        - organization.silicon.webhook.deleted.v1
        - organization.silicon.webhook_subscription.updated.v1
        - organization.silicon.webhook_subscription.deleted.v1
        - sso.setup_link.created.v1
        - sso.configuration.disabled.v1
        - sso.entitlement.replaced.v1
        - sso.connection.activated.v1
        - sso.connection.deactivated.v1
        - sso.connection.deleted.v1
    SiliconWebhookEvent:
      allOf:
        - { $ref: '#/components/schemas/WebhookEvent' }
        - type: object
          required: [event_type]
          properties:
            event_type: { $ref: '#/components/schemas/SiliconFullEventType' }
    ApplicationAdminDecision:
      type: object
      required: [decision]
      additionalProperties: false
      description: >-
        Platform transition for an application that is already in use.
        Applications arrive verified, so this suspends, reactivates, rejects or
        deletes one rather than admitting it. delete is terminal, requires all
        application administration capabilities plus verified-channel step-up,
        and accepts reason only; approved_scopes is rejected.
      properties:
        decision: { type: string, enum: [approve, reject, suspend, reactivate, delete, approve_pending_changes, reject_pending_changes] }
        approved_scopes: { type: array, uniqueItems: true, items: { type: string } }
        reason: { type: [string, 'null'], maxLength: 2000 }
    ApplicationOboEndpoint:
      type: object
      required: [endpoint_id, path, metadata, critical]
      additionalProperties: false
      description: Callable endpoint definition configurable only by a current owner/admin of the Application's organization.
      properties:
        critical: { type: boolean, description: Whether calling this endpoint requires approval from the audience application. }
        endpoint_id:
          type: string
          minLength: 3
          maxLength: 128
          pattern: '^[a-z][a-z0-9_.:-]{2,127}$'
          description: Stable identifier; an existing identifier cannot be assigned a different path.
        path:
          type: string
          minLength: 1
          maxLength: 2048
          pattern: '^/(?!/)(?!.*(?:^|/)\.\.?(?:/|$))(?!.*[?#\x00-\x20\x7F]).*$'
          description: Stable absolute audience-application endpoint path.
        metadata:
          type: object
          additionalProperties: true
          description: Each top-level key is required at exchange. A descriptor may declare type as string, number, integer, boolean, object, array, or null; size, nesting, and node count are bounded.
    OboEndpointReference:
      type: object
      required: [endpoint_id, path]
      additionalProperties: false
      properties:
        endpoint_id: { type: string, pattern: '^[a-z][a-z0-9_.:-]{2,127}$' }
        path: { type: string, maxLength: 2048 }
    OboApplicationReference:
      type: object
      required: [app_id, org_id]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        org_id: { $ref: '#/components/schemas/OrgId' }
    OboEndpointCatalog:
      type: object
      required: [application, endpoints]
      additionalProperties: false
      properties:
        application: { $ref: '#/components/schemas/OboApplicationReference' }
        endpoints:
          type: array
          maxItems: 50
          description: Active definitions ordered by endpoint_id.
          items: { $ref: '#/components/schemas/ApplicationOboEndpoint' }
    OboRequestMethod:
      type: string
      minLength: 1
      maxLength: 32
      pattern: "^[A-Z][A-Z0-9!#$%&'*+.^_`|~-]{0,31}$"
      description: Canonical uppercase HTTP method included byte-for-byte in the OBO HMAC and proof binding.
    OboBodySha256:
      type: string
      pattern: '^[0-9a-f]{64}$'
      description: Lowercase hexadecimal SHA-256 digest of the exact downstream request body bytes.
    OboExchangeRequestBinding:
      type: object
      required: [method, body_sha256]
      additionalProperties: false
      properties:
        method: { $ref: '#/components/schemas/OboRequestMethod' }
        body_sha256: { $ref: '#/components/schemas/OboBodySha256' }
    OboVerifyRequestBinding:
      type: object
      required: [method, path, body_sha256]
      additionalProperties: false
      properties:
        method: { $ref: '#/components/schemas/OboRequestMethod' }
        path:
          type: string
          minLength: 1
          maxLength: 2048
          pattern: '^/(?!/)(?!.*(?:^|/)\.\.?(?:/|$))(?!.*[?#\x00-\x20\x7F]).*$'
          description: Exact registered path of the downstream request.
        body_sha256: { $ref: '#/components/schemas/OboBodySha256' }
    OboExchangeRequest:
      type: object
      required: [subject_token, audience, endpoint_id, metadata, request]
      additionalProperties: false
      description: Delegates as the same user to any declared and consented audience; org_id selects a user-authorized organization when more than one is available.
      properties:
        org_id: { $ref: '#/components/schemas/OrgId' }
        subject_token: { type: string, minLength: 32, maxLength: 4096, description: Actor-bound application access token issued to the calling app. }
        audience: { $ref: '#/components/schemas/AppId' }
        endpoint_id: { type: string, minLength: 3, maxLength: 128, pattern: '^[a-z][a-z0-9_.:-]{2,127}$' }
        metadata:
          type: object
          additionalProperties: true
          description: Exact metadata bound into the proof. It must contain every registered key, no unregistered keys, and values matching every declared type.
        request: { $ref: '#/components/schemas/OboExchangeRequestBinding' }
    OboProofResponse:
      type: object
      required: [access_proof, proof_id, expires_in, expires_at]
      additionalProperties: false
      description: The idempotency replay envelope for this secret response expires no later than expires_at.
      properties:
        testing_context: { $ref: '#/components/schemas/OboTestingContext' }
        access_proof: { type: string, pattern: '^obo_[A-Za-z0-9_-]{43}$' }
        proof_id: { type: string, format: uuid }
        expires_in: { type: integer, minimum: 1, maximum: 60, default: 60 }
        expires_at: { type: string, format: date-time }
    OboVerifyRequest:
      type: object
      required: [access_proof, request]
      additionalProperties: false
      description: Exact actual-request binding; successful verification is strictly single-use and not idempotently replayable.
      properties:
        access_proof: { type: string, pattern: '^obo_[A-Za-z0-9_-]{43}$' }
        request: { $ref: '#/components/schemas/OboVerifyRequestBinding' }
    OboAccessResult:
      type: object
      required: [valid, proof_id, issuer_app_id, audience, actor, authorization, org_id, endpoint, metadata, expires_at, consumed_at]
      properties:
        valid: { const: true }
        proof_id: { type: string, format: uuid }
        issuer_app_id: { $ref: '#/components/schemas/AppId' }
        audience: { $ref: '#/components/schemas/AppId' }
        actor: { $ref: '#/components/schemas/ActorRef' }
        authorization: { $ref: '#/components/schemas/ApplicationAuthorization' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        endpoint: { $ref: '#/components/schemas/OboEndpointReference' }
        metadata: { type: object, additionalProperties: true }
        expires_at: { type: string, format: date-time }
        consumed_at: { type: string, format: date-time }

    ApplicationScope:
      type: object
      required: [iam, external]
      additionalProperties: false
      properties:
        iam: { type: array, uniqueItems: true, items: { type: string }, description: IAM permissions; defaults to self.identity.read and self.profile.read on creation. }
        external: { type: array, items: { $ref: '#/components/schemas/ApplicationExternalScope' } }
    ApplicationExternalScope:
      type: object
      required: [app_id, endpoint_id]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        endpoint_id: { type: string }
    ApplicationConsentScope:
      type: object
      required: [scope, description, critical]
      properties:
        scope: { type: string }
        description: { type: string }
        critical: { type: boolean }
        app_id: { type: [string, 'null'] }
    ApplicationScopeRequestCreate:
      type: object
      required: [app_scope, message]
      additionalProperties: false
      properties:
        app_scope: { $ref: '#/components/schemas/ApplicationScope' }
        message: { type: string, minLength: 1, maxLength: 10000 }
    ApplicationScopeMessageCreate:
      type: object
      required: [message]
      additionalProperties: false
      properties:
        message: { type: string, minLength: 1, maxLength: 10000 }
    ApplicationScopeDecision:
      type: object
      required: [decision]
      additionalProperties: false
      properties:
        decision: { type: string, enum: [approve, deny] }
        reason: { type: string, maxLength: 10000, description: Required and nonempty when denying a request. }
    ApplicationTestingEnvironmentCreate:
      type: object
      required: [name]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 200 }
        description: { type: string, maxLength: 2000 }
        iam_test_key: { type: string, description: Existing IAM testing key; absent creates a new environment, invalid is rejected. }
    ApplicationTestingEnvironmentCreated:
      type: object
      required: [environment_id, org_id, name, iam_test_key, app_id, app_secret, dependencies, secret_replay_expires_at]
      properties:
        environment_id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string }
        description: { type: [string, 'null'] }
        iam_test_key: { type: string }
        app_id: { $ref: '#/components/schemas/AppId' }
        app_secret: { type: string }
        dependencies: { type: array, items: { $ref: '#/components/schemas/AppId' } }
        secret_replay_expires_at: { type: string, format: date-time }
    ApplicationTestingEnvironment:
      type: object
      required: [environment_id, org_id, name, last_activity_at, retention_days]
      properties:
        environment_id: { type: string, format: uuid }
        org_id: { $ref: '#/components/schemas/OrgId' }
        name: { type: string }
        description: { type: [string, 'null'] }
        last_activity_at: { type: string, format: date-time }
        retention_days: { type: integer, minimum: 1 }
    ApplicationTestingEnvironmentPage:
      type: object
      required: [items, page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/ApplicationTestingEnvironment' } }
        page: { $ref: '#/components/schemas/PageInfo' }

    ApplicationScopeCatalog:
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/ApplicationConsentScope' } }
    ApplicationScopeMessage:
      type: object
      required: [id, author, message, created_at]
      properties:
        id: { type: string, format: uuid }
        author: { $ref: '#/components/schemas/ApplicationScopeMessageAuthor' }
        message: { type: string }
        created_at: { type: string, format: date-time }
    ApplicationScopeRequest:
      type: object
      required: [id, app_id, scopes, status, version, created_at, updated_at, messages, can_decide]
      properties:
        id: { type: string, format: uuid }
        app_id: { $ref: '#/components/schemas/AppId' }
        target_app_id: { type: [string, 'null'] }
        scopes: { type: array, items: { type: string } }
        status: { type: string, enum: [pending, approved, denied, superseded] }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        messages: { type: array, items: { $ref: '#/components/schemas/ApplicationScopeMessage' } }
        can_decide: { type: boolean }
    ApplicationScopeRequestList:
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/ApplicationScopeRequest' } }
    ApplicationBundleCreate:
      type: object
      required: [org_id, app_id, app_ids]
      additionalProperties: false
      properties:
        org_id: { $ref: '#/components/schemas/OrgId' }
        app_id: { $ref: '#/components/schemas/ApplicationHandle' }
        app_name: { type: string, maxLength: 200 }
        app_logo: { type: string, format: uri, maxLength: 2048 }
        app_ids: { type: array, minItems: 1, maxItems: 100, uniqueItems: true, items: { $ref: '#/components/schemas/AppId' } }
    ApplicationBundlePatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        app_name: { type: [string, 'null'], maxLength: 200 }
        app_logo: { type: [string, 'null'], format: uri, maxLength: 2048 }
        app_ids: { type: array, minItems: 1, maxItems: 100, uniqueItems: true, items: { $ref: '#/components/schemas/AppId' } }
    ApplicationBundle:
      type: object
      required: [id, bundle_id, org_id, app_ids, version, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        bundle_id: { $ref: '#/components/schemas/AppId' }
        org_id: { $ref: '#/components/schemas/OrgId' }
        app_name: { type: [string, 'null'] }
        app_logo: { type: [string, 'null'], format: uri }
        app_ids: { type: array, items: { $ref: '#/components/schemas/AppId' } }
        version: { type: integer, minimum: 1 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ApplicationBundleList:
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/ApplicationBundle' } }
    ApplicationBundleLoginOrganizations:
      type: object
      required: [bundle, items]
      properties:
        bundle: { $ref: '#/components/schemas/ApplicationBundle' }
        items: { type: array, items: { $ref: '#/components/schemas/LoginOrganizations' } }

    ApplicationWebhookScope:
      type: string
      enum: [full, membership, updates, trust]

    ApplicationScopeMessageAuthor:
      type: object
      required: [principal_id, type, public_id]
      properties:
        principal_id: { type: string, format: uuid }
        type: { type: string, enum: [carbon, silicon, system] }
        public_id: { type: string }

    OboTestingContext:
      type: object
      required: [app_id, app_secret, iam_test_key]
      additionalProperties: false
      properties:
        app_id: { $ref: '#/components/schemas/AppId' }
        app_secret: { type: string, description: Audience test credential forwarded only for this isolated downstream request. }
        iam_test_key: { type: string }

    ApplicationReadObject:
      type: object
      additionalProperties: true
      description: Scope-filtered application bearer response. Fields absent from approved user scopes are omitted. An omitted field is undisclosed and grants no authority.
