> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lumos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Update Entitlement

> Update an entitlement's `label`, `description_override` and `custom_approval_message_override`. Fields you omit are left unchanged. Any other field, including the read-only `description`, returns 400.

Only entitlements created in Lumos (`source` is `manual`) can be renamed; sending a new `label` for a synced one returns 400 and changes nothing, while its current `label` is accepted and ignored. A `description_override` is stored as sent and kept across syncs, on synced entitlements too; `null` removes it, and is accepted and ignored when none is set. A non-null `custom_approval_message_override` on an entitlement that can't be requested returns 422 with code `entitlement.not_requestable`, while `null` is accepted and ignored.

Returns 404 if the entitlement is unknown in the caller's organization.



## OpenAPI

````yaml /api-reference/lumos-api-v1.json patch /v1/entitlements/{entitlement_uuid}
openapi: 3.1.0
info:
  title: Lumos API (v1)
  description: >

    Lumos API v1 is the programmatic interface to your connected apps, the
    entitlements on them, and

    the workflows that grant access to those entitlements.


    Every path is prefixed with `/v1`. Authenticate with an API token, which
    inherits the permissions

    of the user who created it: a call returns what that user could see in the
    Lumos UI, and nothing

    more.


    New here? Browse what you can connect with `GET /v1/integrations`, read the
    credentials an

    integration expects with `GET
    /v1/integrations/{app_class_id}/connection-schema`, then connect it

    with `POST /v1/apps`.


    Every list endpoint shares one pagination contract. Set the page length with
    `page_size`, then send

    the `next_page_token` from a response back as `page_token` to fetch the next
    page. A `null`

    `next_page_token` means you have reached the end.


    Every error shares one envelope. Branch on the machine-readable `code`, not
    on `detail`.
  version: 0.1.0
servers:
  - url: https://api.lumos.com
security: []
tags:
  - name: Access Policies
    description: >

      An access policy grants apps, and optionally specific entitlements on
      them, to the identities that

      qualify under its `access_condition`, a Lumos Condition object.


      `POST /v1/access-policies` creates a policy and returns 201. `GET
      /v1/access-policies` lists

      policies, and `GET`, `PUT`, and `DELETE` on
      `/v1/access-policies/{access_policy_id}` read, replace,

      and delete one.


      Each entitlement a policy grants appears under `apps[].entitlements[]`,
      identified by its

      Entitlement UUID, the `id` that `GET /v1/entitlements/{entitlement_uuid}`
      returns.
  - name: Access Requests
    description: >

      An access request asks for access to an app, or to specific entitlements
      on it, for you or for

      another user.


      - `POST /v1/access-requests` creates requests, naming entitlements by
      Entitlement UUID in
        `entitlement_ids`, and returns them as a list.
      - `GET /v1/access-requests` and `GET /v1/access-requests/{id}` read
      requests.

      - `POST /v1/access-requests/{id}:cancel` cancels a pending request.


      Of the built-in roles, Admin, Read Only Admin, AppStore Read Only Admin,
      and App Admin can read

      every access request in your organization. Anyone else sees only the
      requests they made and the

      ones made for them.
  - name: Access Reviews
    description: >

      An access review is a campaign that reviews who has access to what across
      your connected apps.

      Each app in a campaign is an app review.


      v1 has two access-review endpoints so far, both scoped to one app review:


      - `GET
      /v1/access-reviews/{review_uuid}/apps/{app_review_uuid}/subject-filters`
      lists the fields
        you can filter the app review's subjects on. Each field comes with the operators it supports
        and, when it has a fixed set of values, the values it accepts.
      - `POST /v1/access-reviews/{review_uuid}/apps/{app_review_uuid}:sync` runs
      the app review's data
        import, optionally with new scope filters. It returns 202 with an operation. Poll
        `GET /v1/operations/{operation_id}` until `done` is true.

      You can't create, list, or update campaigns in v1 yet. Lumos enables these
      endpoints per

      organization, separately from the rest of v1, so contact your Lumos
      representative to turn them on.
  - name: Accounts
    description: >

      An account is a person's presence inside a connected app. Accounts arrive
      through sync; you cannot

      create one through this API.


      `GET /v1/accounts/{account_uuid}/entitlements` lists what one account
      holds. It reads the same

      relationship as `GET /v1/entitlements/{entitlement_uuid}/accounts`, from
      the account's side rather

      than the entitlement's.
  - name: Activity Logs
    description: >

      Activity logs record the actions taken across your organization's apps and
      identities.

      `GET /v1/activity-logs` lists them, newest first.


      Reading activity logs requires permission to view them across your
      organization. Of the built-in

      roles, Admin and Read Only Admin have it. Without it, the request returns
      403.
  - name: App Categories
    description: >

      App categories are the fixed set of categories an app can belong to, such
      as `Developers`.

      `GET /v1/app-categories` lists them as `{label}` objects.


      A `label` is the exact value to send as `category` to `POST /v1/apps` and

      `PATCH /v1/apps/{app_uuid}`. It is also the value the app read endpoints
      report. Read the list

      instead of hard-coding it.
  - name: Apps
    description: >

      An app is an integration you have connected, one per connected instance of
      a service. Your Okta

      tenant is an app. A second Slack workspace is a second app.


      ### Connecting one


      1. `GET /v1/integrations/{app_class_id}/connection-schema` to see which
      `auth` and `settings`
         keys the integration expects.
      2. `POST /v1/apps` with those credentials. Apps whose credentials validate
      right away connect, and
         their first sync waits for an admin to review match rules. It starts from the Lumos UI, or from
         `POST /v1/apps/{app_uuid}:sync` with `skip_first_sync_review: true`. Apps that need browser
         consent are finished in the Lumos UI.
      3. `GET /v1/apps/{app_uuid}` to follow the first sync. `sync_status` shows
      the state of the
         app's most recent sync. A sync started with `POST /v1/apps/{app_uuid}:sync` also returns an
         operation, which you poll with `GET /v1/operations/{operation_id}`.
      4. `POST /v1/apps/{app_uuid}:sync` to resync later. Only one sync runs per
      app at a time, so a
         second request while one is in flight returns 409.

      Rotate credentials with `PATCH /v1/apps/{app_uuid}`, which also updates an
      app's metadata.

      `POST /v1/apps/{app_uuid}:disconnect` drops the stored credentials and
      returns the app to a

      connectable state; `DELETE /v1/apps/{app_uuid}` removes the app outright.


      ### Reading apps


      `expand` is opt-in and evaluated per field: ask for `admins`,
      `custom_attributes`,

      `appstore_settings`, or any of the three workflow fields. A field you lack
      permission to read is

      left out of the response instead of failing the whole request, so a 200
      does not prove every field

      you asked for came back.


      To list only the connected apps you manage through the API, combine
      `connection_source=api` with

      `disconnected=false`.


      ### Bound workflows


      Each app carries three bindings: an approval workflow, a default
      provisioning workflow, and a

      default deprovisioning workflow. The two `default_` workflows are the ones
      that entitlements on

      the app fall back to. To bind a workflow to one entitlement instead, use
      the Entitlements

      endpoints.
  - name: Custom Attribute Definitions
    description: >

      Custom attributes are the extra fields your organization keeps on users.
      These endpoints read

      their *definitions*, not the values stored against any particular user.


      `GET /v1/custom-attribute-definitions` returns one entry per label,
      unified across every app that

      supplies it. Narrow the list with `source_type` to tell attributes synced
      from an app apart from

      ones your team created, or with `app_id` to see only what a single app
      supplies.


      `GET /v1/custom-attribute-definitions/{id}` reads one definition by its
      `id`, and

      `GET /v1/custom-attribute-definitions/{id}/configs` lists the configs
      behind it, highest priority

      first.
  - name: Entitlements
    description: >

      An entitlement is a grantable unit of access on an app: a role, a license
      tier, a group, or

      another access right. Accounts hold entitlements.


      - `POST /v1/entitlements` creates a requestable entitlement on an app.

      - `GET /v1/apps/{app_uuid}/entitlements` lists the entitlements on one
      app.

      - `GET /v1/entitlements/{entitlement_uuid}/accounts` lists who currently
      holds one.

      - `PATCH /v1/entitlements/{entitlement_uuid}` updates its `label`,
      `description`, and
        `custom_approval_message_override`. Sending any other field returns 400.
      - `DELETE /v1/entitlements/{entitlement_uuid}` refuses while the
      entitlement is still held,
        returning 409 with an `active_accounts` count.

      ### Per-entitlement workflows


      An entitlement can have its own approval override, provisioning workflow,
      and deprovisioning

      workflow, bound to it rather than to the whole app. Each has a GET and a
      PUT, and the approval

      override also has a DELETE that turns it off.


      An entitlement that can't be requested returns 404 with

      `Workflows are not available for this entitlement.` on each of these
      endpoints.
  - name: Groups
    description: >

      Lumos syncs groups from your connected apps, such as your Okta groups, and
      these endpoints read

      them.


      - `GET /v1/groups` lists groups. Filter by `name` (add `exact_match=true`
      to require an exact
        match), by `integration_specific_id` (the group's ID in the source system), or by `app_id`.
      - `GET /v1/groups/{group_id}` reads one group, and `GET
      /v1/groups/{group_id}/users` lists its
        members.

      An `app_id` that matches no app in your organization returns 404.
  - name: Identity Events
    description: >

      Identity events are the changes Lumos detects on your organization's
      identities, such as a change

      to a status, a group membership, or a custom attribute. `GET
      /v1/identity-events` lists them.

      Narrow the list with `identity_ids`, `changed_fields`, `start_time`, and
      `end_time`.


      A page can come back empty while `next_page_token` is still set, because
      events Lumos can't read

      are dropped after a page is fetched. Keep paging until `next_page_token`
      is absent, not until a

      page is empty.
  - name: Inline Webhooks
    description: >

      Inline webhooks are the webhooks your organization has configured in
      Lumos.

      `GET /v1/inline-webhooks` lists all of them, whatever their type.


      A webhook's `type` says where Lumos uses it.
  - name: Integrations
    description: >

      An integration is a service Lumos knows how to connect to, identified by
      an `app_class_id` such as

      `okta.com` or `workspace.google.com`. This is the catalog you connect
      *from*. Apps you have

      already connected are read through the Apps endpoints.


      Only connectable integrations appear here. Software discovered in your
      environment that has no

      Lumos integration behind it is excluded.


      ### Discovering how to connect


      1. `GET /v1/integrations?query=google` to find the integration. The filter
      matches both the
         `app_class_id` and the display name, so `google` returns `workspace.google.com` and
         `cloud.google.com`.
      2. `GET /v1/integrations/{app_class_id}/connection-schema` for the JSON
      schema of the `auth` and
         `settings` fields it expects.
      3. `POST /v1/apps` with those values.


      Step 2 is the point of this group: it lets you build a connect form
      without hardcoding one

      credential shape per integration.


      `GET /v1/integrations/{app_class_id}` and the connection schema return 404
      for any integration the

      list leaves out. The connection schema also returns 404 for an integration
      whose schema Lumos

      cannot describe yet.
  - name: Knowledge
    description: >

      Knowledge entries are the documents and notes Lumos agents draw on when
      they answer access

      questions. Each entry carries a title, body, tags, and product scopes,
      plus an optional file

      attachment.


      Because of that attachment, create and update send `multipart/form-data`
      rather than JSON. On

      update, tag and scope lists replace what was there instead of appending,
      and `clear_attachment=true`

      removes an existing file without uploading a replacement.


      `DELETE` deletes a knowledge entry. It no longer appears in list results,
      and you can't restore

      it through the API.


      Pages here are small: `page_size` defaults to 10 and is clamped to 25.
  - name: Lifecycle Management
    description: >

      Lifecycle management covers user offboardings. An offboarding runs your
      organization's

      offboarding workflow for one user, either right away or at a scheduled
      time.


      - `POST /v1/lifecycle-management/user-offboardings` starts an offboarding
      now, or at
        `scheduled_at` when you send a future time.
      - `GET /v1/lifecycle-management/user-offboardings/{user_offboarding_id}`
      reads one.

      - `POST
      /v1/lifecycle-management/user-offboardings/{user_offboarding_id}:cancel`
      cancels one that
        is scheduled or in progress.

      There is no list endpoint, so keep the `id` that create returns.


      `workflow_config_id` names an offboarding workflow configuration, not a
      workflow from

      `/v1/workflows`. Omit it to use the configuration your organization has
      selected for automatic

      offboarding. Only organizations with the multiple offboarding workflows
      feature can send it. For

      any other organization, a request that includes it returns 400.
  - name: Meta
    description: >

      `GET /v1/info` returns the API's current git revision, and `GET /v1/art`
      returns Lumos ASCII art.

      Use either to check your setup.


      Both require an API token. A 200 means your token works.
  - name: Operations
    description: >

      An operation tracks work that continues after the request that started it
      has returned. These

      endpoints start such work and respond with 202 and an operation:


      - `POST /v1/apps/{app_uuid}:sync`

      - `POST /v1/accounts:upload`

      - `POST /v1/access-reviews/{review_uuid}/apps/{app_review_uuid}:sync`


      Poll `GET /v1/operations/{operation_id}` until `done` is true. While
      `done` is false, neither

      `result` nor `error` is set. Once `done` is true, exactly one of them is.


      An app-sync operation shows the app's most recent sync, which can be a
      later run than the one you

      started.
  - name: Pre-Approval Rules
    description: >

      A pre-approval rule pre-approves matching access requests for an app.


      - `GET /v1/apps/{app_uuid}/pre-approval-rules` lists an app's rules, and
      `POST` on the same path
        creates one.
      - `GET`, `PATCH`, and `DELETE` on
      `/v1/apps/{app_uuid}/pre-approval-rules/{pre_approval_rule_id}`
        read, update, and delete one.

      If the app has been merged into another app, these endpoints work on the
      rules of the app it was

      merged into.


      Lumos enables condition-based pre-approval rules per organization,
      separately from the rest of v1,

      so contact your Lumos representative to turn them on.
  - name: Tasks
    description: >

      A task is work Lumos assigns to a person or group, such as approving an
      access request or

      provisioning access by hand. A workflow that creates a task waits for
      someone to act on it before

      it continues.


      ### Finding tasks


      `GET /v1/tasks` lists the tasks visible to you. `show_all_tasks=true`
      lists every task in your

      organization instead. It needs permission to view every task, which the
      built-in Admin, Read Only

      Admin, and AppStore Read Only Admin roles have, and which a custom role
      can also grant. Anyone

      else gets a 403.


      Filter with `type`, `category`, `status`, and `product_area`, whose values
      are lowercase and

      kebab-case. For example,

      `GET /v1/tasks?category=approval&product_area=appstore&status=pending`
      lists pending AppStore

      approvals.


      A task names the entitlement it grants or removes in `entitlement_id`, an
      Entitlement UUID.


      ### Acting on a task


      - `POST /v1/tasks/{task_id}:complete` approves an approval task, or
      completes another task with
        its one completion action.
      - `POST /v1/tasks/{task_id}:dismiss` denies an approval task, or dismisses
      another task without
        doing its work.
      - `POST /v1/tasks/{task_id}:perform-action` takes a specific action.
        `GET /v1/tasks/{task_id}/actions` lists the actions you can take and the statuses each one can
        move the task to, which are the values `transition_to` accepts.
      - `POST /v1/tasks/{task_id}:reassign` changes who the task is assigned to.
  - name: Users
    description: >

      Users are the people in your organization. These endpoints read them, and
      the only thing they

      change is a user's Lumos roles.


      - `GET /v1/users` lists users. `search` matches names and emails, and
      `exact_match=true`
        requires an exact match.
      - `GET /v1/users/current` returns the user who owns your API token.

      - `GET /v1/users/{user_id}` reads one user, and `GET
      /v1/users/{user_id}/accounts` lists the
        accounts that user holds.
      - `GET /v1/users/{user_id}/roles` lists a user's Lumos roles. Each role
      has an `id` (a built-in
        role's kebab-case name such as `app-admin`, or a custom role's UUID), a display `label`,
        `is_custom`, and a `resource_type` that is null for roles that apply across the organization.
        `POST /v1/users/{user_id}/roles/{role_id}` assigns one, and
        `DELETE /v1/users/{user_id}/roles/{role_id}` removes one, where `role_id` is the role's `id`.
        Assigning a role the user already has, or removing one they don't have, changes nothing.

      A user who has been deleted returns the same 404 as one who doesn't exist
      in your organization.
  - name: Workflows
    description: >

      A workflow is a reusable definition of the steps Lumos runs to approve a
      request, provision access,

      or deprovision it. Create the definition here, then bind it to an app or
      an entitlement through

      their own workflow endpoints.


      ### Building one


      1. `GET /v1/workflow-steps?app_id={app_uuid}&type={workflow type}` to
      discover the steps available
         for that app in the type of workflow you will create, each with the input schema it accepts.
         Without `type`, it lists only the steps every workflow type accepts.
      2. `POST /v1/workflows` with the steps you want.

      3. Bind it, for example with `PUT
      /v1/apps/{app_uuid}/default-provisioning-workflow`.


      `type` and `app_id` are fixed at creation. `PUT
      /v1/workflows/{workflow_id}` returns 400 for a

      change to either.


      ### Workflows this API cannot describe


      Some workflows Lumos builds for you, including the out-of-the-box
      provisioning defaults, contain

      steps with no public representation. `GET /v1/workflows` skips them, so a
      page can hold fewer items

      than `page_size` while more results remain. Treat an absent
      `next_page_token`, not a short page, as

      the end of the list. Binding one of these to an app or entitlement returns
      400.
paths:
  /v1/entitlements/{entitlement_uuid}:
    patch:
      tags:
        - Entitlements
      summary: Update Entitlement
      description: >-
        Update an entitlement's `label`, `description_override` and
        `custom_approval_message_override`. Fields you omit are left unchanged.
        Any other field, including the read-only `description`, returns 400.


        Only entitlements created in Lumos (`source` is `manual`) can be
        renamed; sending a new `label` for a synced one returns 400 and changes
        nothing, while its current `label` is accepted and ignored. A
        `description_override` is stored as sent and kept across syncs, on
        synced entitlements too; `null` removes it, and is accepted and ignored
        when none is set. A non-null `custom_approval_message_override` on an
        entitlement that can't be requested returns 422 with code
        `entitlement.not_requestable`, while `null` is accepted and ignored.


        Returns 404 if the entitlement is unknown in the caller's organization.
      operationId: updateEntitlement
      parameters:
        - name: entitlement_uuid
          in: path
          required: true
          schema:
            type: string
            description: >-
              The entitlement's ID, which `GET /v1/entitlements` and `GET
              /v1/apps/{app_uuid}/entitlements` return as `id`.
            title: Entitlement Uuid
          description: >-
            The entitlement's ID, which `GET /v1/entitlements` and `GET
            /v1/apps/{app_uuid}/entitlements` return as `id`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EntitlementUpdateInput'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EntitlementOutput'
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Not Found
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Conflict
        '422':
          description: >-
            Unprocessable — a non-null `custom_approval_message_override` was
            sent for an entitlement that can't be requested (code
            `entitlement.not_requestable`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Too Many Requests
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
          description: Internal Server Error
      security:
        - HTTPBearer: []
components:
  schemas:
    EntitlementUpdateInput:
      properties:
        label:
          type: string
          title: Label
          description: >-
            New human-readable label for the entitlement. Only entitlements
            created in Lumos (`source` is `manual`) can be renamed; the
            integration owns a synced entitlement's label.
        description_override:
          anyOf:
            - type: string
              maxLength: 2000
              minLength: 1
            - type: 'null'
          title: Description Override
          description: >-
            Description shown to requesters in place of the integration's, kept
            as sent and across syncs. `null` removes it, and the integration's
            own description returns on its next sync; when none is set, `null`
            changes nothing.
        custom_approval_message_override:
          anyOf:
            - type: string
              maxLength: 511
              minLength: 1
            - type: 'null'
          title: Custom Approval Message Override
          description: >-
            Message sent when a request is approved. `null` inherits the app's
            message.
      additionalProperties: false
      type: object
      title: EntitlementUpdateInput
      description: Request body for `PATCH /v1/entitlements/{entitlement_uuid}`.
      examples:
        - label: Viewer
    EntitlementOutput:
      properties:
        id:
          type: string
          title: Id
          description: The ID of this entitlement.
        app_id:
          type: string
          title: App Id
          description: The ID of the app that owns this entitlement.
        label:
          type: string
          title: Label
          description: >-
            Human-readable label for the entitlement (e.g. a role or license
            name).
        entitlement_type_label:
          type: string
          title: Entitlement Type Label
          description: >-
            Human-readable label for the entitlement's type (e.g. 'Role',
            'License', 'Group').
        source:
          type: string
          enum:
            - google-workspace-quick-inbox
            - google-workspace-deep-inbox
            - microsoft-deep-inbox
            - microsoft-oauth
            - microsoft-gcc
            - google-workspace-oauth
            - okta
            - manual
            - onelogin
            - atlassian
            - google-cloud
            - integration
            - jumpcloud
            - custom
            - mcp
            - native
          title: Source
          description: >-
            How this entitlement was discovered. `manual` is created in Lumos;
            other values come from an upstream integration.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            The description shown to requesters: `description_override` if set,
            otherwise the integration's own description. Read-only; set
            `description_override` instead.
        description_override:
          anyOf:
            - type: string
            - type: 'null'
          title: Description Override
          description: >-
            The description set in Lumos, kept across syncs. Null when none is
            set, and the entitlement shows its integration's description.
        approval_workflow_override:
          anyOf:
            - $ref: '#/components/schemas/Workflow'
            - $ref: '#/components/schemas/WorkflowRef'
            - type: 'null'
          title: Approval Workflow Override
          description: >-
            The entitlement's approval workflow override. Returns {id} by
            default; expand with expand=approval_workflow_override for the full
            workflow.
        provisioning_workflow:
          anyOf:
            - $ref: '#/components/schemas/Workflow'
            - $ref: '#/components/schemas/WorkflowRef'
            - type: 'null'
          title: Provisioning Workflow
          description: >-
            The entitlement's provisioning workflow. Returns {id} by default;
            expand with expand=provisioning_workflow for the full workflow.
        deprovisioning_workflow:
          anyOf:
            - $ref: '#/components/schemas/Workflow'
            - $ref: '#/components/schemas/WorkflowRef'
            - type: 'null'
          title: Deprovisioning Workflow
          description: >-
            The entitlement's deprovisioning workflow. Returns {id} by default;
            expand with expand=deprovisioning_workflow for the full workflow.
        custom_approval_message_override:
          anyOf:
            - type: string
            - type: 'null'
          title: Custom Approval Message Override
          description: >-
            This entitlement's own custom approval message, shown to requesters
            when the entitlement is approved, overriding the app's. Null means
            this entitlement inherits the app's approval message.
        appstore_settings:
          anyOf:
            - $ref: '#/components/schemas/EntitlementAppStoreSettings'
            - type: 'null'
          description: >-
            The entitlement's AppStore-catalog settings. Null unless expanded
            with expand=appstore_settings. Omitted entirely (not present, not
            null) when the parent app isn't enrolled in the AppStore.
      type: object
      required:
        - id
        - app_id
        - label
        - entitlement_type_label
        - source
      title: EntitlementOutput
      description: Public-API representation of an Entitlement.
    ApiErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/ApiError'
          description: The error that caused this response.
      type: object
      required:
        - error
      title: ApiErrorEnvelope
      description: >-
        The V1 error response body. Every error nests under `error`, so an error
        body is

        recognizable by its shape, not only its status code.
    Workflow:
      properties:
        id:
          type: string
          title: Id
          description: A UUID identifying this Workflow.
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: A name for this Workflow.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: A description of this Workflow.
        type:
          type: string
          enum:
            - approval
            - approval-chain
            - entitlement-provisioning
            - entitlement-deprovisioning
            - account-provisioning
            - account-deprovisioning
          title: Type
          description: The kind of workflow.
        app_id:
          anyOf:
            - type: string
            - type: 'null'
          title: App Id
          description: UUID of the app this Workflow belongs to, if associated.
        steps:
          items:
            $ref: '#/components/schemas/WorkflowStepOutput'
          type: array
          title: Steps
          description: The ordered steps of the workflow.
      type: object
      required:
        - id
        - type
        - steps
      title: Workflow
      description: Response shape for a Workflow.
    WorkflowRef:
      properties:
        id:
          type: string
          title: Id
          description: UUID of the bound workflow.
      type: object
      required:
        - id
      title: WorkflowRef
      description: |-
        A reference to a bound workflow — shaped `{ "id": "..." }` so the path
        `<field>.id` is stable whether or not the field is expanded.
    EntitlementAppStoreSettings:
      properties:
        visible_in_appstore:
          type: boolean
          title: Visible In Appstore
          description: Whether this entitlement is visible in the AppStore catalog.
        allowed_requester_group_uuids_override:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: Allowed Requester Group Uuids Override
          description: >-
            UUIDs of groups whose members may request this entitlement,
            overriding the app's allowed requester groups. `null` means this
            entitlement inherits the app's allowed requester groups. An empty
            list means the override is on with no group restriction: anyone can
            request this entitlement, even if the app restricts requesters.
        time_based_access_override:
          anyOf:
            - $ref: '#/components/schemas/TimeBasedAccessOverride'
            - type: 'null'
          description: >-
            This entitlement's own time-based access options. Null means this
            entitlement inherits the app's time-based access options.
        request_validation_webhook_uuid:
          anyOf:
            - type: string
            - type: 'null'
          title: Request Validation Webhook Uuid
          description: >-
            UUID of the inline webhook called to validate access requests for
            this entitlement. `null` means no webhook is set on this
            entitlement. Nothing is inherited from the app.
      type: object
      required:
        - visible_in_appstore
      title: EntitlementAppStoreSettings
    ApiError:
      properties:
        detail:
          type: string
          title: Detail
          description: Human-readable summary of the error.
        type:
          $ref: '#/components/schemas/ErrorType'
          description: >-
            Category:
            validation|authentication|permission|not_found|conflict|rate_limited|runtime.
        code:
          type: string
          title: Code
          description: Stable machine-readable error code; branch on this, not 'detail'.
        errors:
          items:
            $ref: '#/components/schemas/ApiErrorField'
          type: array
          title: Errors
          description: Field-level errors; empty for non-validation errors.
      type: object
      required:
        - detail
        - type
        - code
      title: ApiError
      description: The V1 error object — the contents of the envelope's `error` field.
    WorkflowStepOutput:
      properties:
        step:
          type: string
          title: Step
          description: Identifier (catalogue slug) for the step to execute.
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Server-derived human-readable name for this step. Read-only.
        inputs:
          type: object
          title: Inputs
          description: The configured inputs for this step.
      type: object
      required:
        - step
        - inputs
      title: WorkflowStepOutput
      description: A single configured step in a workflow (response shape).
    TimeBasedAccessOverride:
      properties:
        options:
          items:
            $ref: '#/components/schemas/TimeBasedAccessOverrideOption'
          type: array
          title: Options
          description: >-
            This entitlement's own time-based access options, overriding the
            app's.
      type: object
      required:
        - options
      title: TimeBasedAccessOverride
    ErrorType:
      type: string
      enum:
        - validation
        - authentication
        - permission
        - not_found
        - conflict
        - rate_limited
        - runtime
      title: ErrorType
    ApiErrorField:
      properties:
        field:
          type: string
          title: Field
          description: Input field this error refers to, e.g. 'steps[2].inputs.group_id'.
        code:
          type: string
          title: Code
          description: Stable machine-readable code for this field error.
        message:
          type: string
          title: Message
          description: Human-readable description of the field error.
      type: object
      required:
        - field
        - code
        - message
      title: ApiErrorField
      description: A single field-level error (used for validation failures).
    TimeBasedAccessOverrideOption:
      properties:
        label:
          type: string
          title: Label
          description: Display label for this time-based access option.
        value_seconds:
          anyOf:
            - type: integer
            - type: 'null'
          title: Value Seconds
          description: >-
            Duration in seconds. Null indicates a permanent (unlimited) access
            option.
      type: object
      required:
        - label
      title: TimeBasedAccessOverrideOption
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer

````