# platform/openapi — OpenAPI 3.1 (generated)
---
openapi: 3.1.0
info:
  title: Accoil Tools Platform API
  version: '1'
paths:
  "/users/sign_in":
    post:
      operationId: sign_in
      summary: Sign in with email and password (a browser session)
      description: 'Sets a session cookie. An agent normally skips this entirely and
        sends Authorization: Bearer <token> instead — see /agent. Rate limited; repeated
        failures are throttled.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    email:
                      type: string
                      format: email
                    password:
                      type: string
                      format: password
                    remember_me:
                      type: string
                      enum:
                      - '0'
                      - '1'
                      default: '0'
                  required:
                  - email
                  - password
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/users/sign_out":
    delete:
      operationId: sign_out
      summary: Sign out (end the browser session)
      description: Bearer-token requests have no session to end; this is for cookie
        sessions.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/users":
    post:
      operationId: sign_up
      summary: Create a user account
      description: Refused when public registration is off (invites still work). A
        personal account is created with the user; email confirmation is requested
        by mail.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    full_name:
                      type: string
                    email:
                      type: string
                      format: email
                    password:
                      type: string
                      format: password
                      description: minimum length is enforced server-side
                  required:
                  - email
                  - password
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/users/password":
    post:
      operationId: request_password_reset
      summary: Email a password-reset link
      description: Always answers as if the address exists (enumeration-safe). Rate
        limited.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    email:
                      type: string
                      format: email
                  required:
                  - email
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    patch:
      operationId: reset_password
      summary: Set a new password using the token from the reset email
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    reset_password_token:
                      type: string
                      description: from the emailed link
                    password:
                      type: string
                      format: password
                    password_confirmation:
                      type: string
                      format: password
                  required:
                  - reset_password_token
                  - password
                  - password_confirmation
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/users/confirmation":
    post:
      operationId: resend_confirmation
      summary: Resend the email-confirmation link
      description: Signed in, the address is ignored and the mail goes to your own
        (pending) address.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    email:
                      type: string
                      format: email
                  required:
                  - email
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/invites/{token}":
    patch:
      operationId: accept_invite
      summary: Accept an account invite (you become a member)
      description: Requires being signed in; the token in the path is the authorization.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/accounts":
    post:
      operationId: create_account
      summary: Create a team account (you become its owner)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
              required:
              - name
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/members/{id}":
    patch:
      operationId: change_member_role
      summary: Change a member's account role
      description: Owner-only, and you may not grant a role above your own.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                account_member:
                  type: object
                  properties:
                    role:
                      type: string
                      enum: &1
                      - owner
                      - admin
                      - member
                      description: 'roles are inclusive: owner ⊃ admin ⊃ member'
                  required:
                  - role
              required:
              - account_member
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: remove_member
      summary: Remove a member from the account
      description: The last owner cannot be removed.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/member_invites":
    post:
      operationId: invite_member
      summary: Invite someone to the account by email
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum: *1
                  default: member
                  description: you may not grant a role above your own
              required:
              - email
              - role
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/member_invites/{token}":
    delete:
      operationId: revoke_invite
      summary: Revoke a pending invite
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/general":
    patch:
      operationId: update_account
      summary: Rename the account (and manage its logo)
      description: The logo itself is a file upload (multipart form field account[logo])
        — not a JSON field.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                account:
                  type: object
                  properties:
                    name:
                      type: string
                remove_logo:
                  type: string
                  enum:
                  - '1'
                  description: optional; removes the current logo
              required:
              - account
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/profile":
    patch:
      operationId: update_profile
      summary: Update your own name and email
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    full_name:
                      type: string
                    email:
                      type: string
                      format: email
                      description: an email change must be confirmed at the NEW address
                        before it takes effect
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/password":
    patch:
      operationId: change_password
      summary: Change your own password
      description: Your other sessions are signed out.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                current_password:
                  type: string
                  format: password
                password:
                  type: string
                  format: password
                  description: minimum 8 characters
              required:
              - current_password
              - password
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/api_tokens":
    post:
      operationId: create_api_token
      summary: Mint a personal API token (shown once, in the response page)
      description: A read-only token cannot call this (or any write) — minting a write
        token requires a session or a write token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                api_token:
                  type: object
                  properties:
                    name:
                      type: string
                    scope:
                      type: string
                      enum:
                      - read
                      - write
                      default: read
                      description: anything unrecognised becomes read
                    expires_in_days:
                      type: string
                      enum:
                      - ''
                      - '30'
                      - '90'
                      - '365'
                      default: ''
                      description: blank never expires
                  required:
                  - name
              required:
              - api_token
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/api_tokens/{id}":
    delete:
      operationId: revoke_api_token
      summary: Revoke a token — anything using it stops working immediately
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/identities/{id}":
    delete:
      operationId: disconnect_identity
      summary: Disconnect a linked social login
      description: Refused while you have no password — a linked provider may be your
        only way in.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/billing/checkouts":
    post:
      operationId: create_checkout_session
      summary: Start a Stripe Checkout for a tool plan
      description: Answers with a redirect to a stripe.com URL; payment happens there
        and the subscription lands via webhook — poll the billing page for it. Refused
        when billing is not configured on the deployment, the plan is not purchasable,
        or the tool already has a live subscription.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                tool_plan_id:
                  type: integer
                  description: an active plan's id — plans are listed on the billing
                    page
              required:
              - tool_plan_id
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/{account_slug}/settings/billing/portal":
    post:
      operationId: open_billing_portal
      summary: Open Stripe's Billing Portal (card, plan changes, invoices)
      description: Answers with a redirect to a stripe.com URL. Requires a prior purchase
        — the first checkout creates the account's Stripe customer.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{slug}/suspend":
    patch:
      operationId: suspend_account
      summary: Suspend an account (it becomes read-only immediately)
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{slug}/reactivate":
    patch:
      operationId: reactivate_account
      summary: Reactivate a suspended account
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{slug}":
    delete:
      operationId: delete_account
      summary: Hard-delete a suspended account — irreversible
      description: Refused unless the account is already suspended. Members, invites,
        products and credentials go; dead letters are kept, orphaned.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/users/{id}":
    patch:
      operationId: set_app_role
      summary: Grant or remove the platform-operator role
      description: You cannot demote yourself, and the last app admin cannot be demoted.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    app_role:
                      type: string
                      enum:
                      - admin
                      - member
                      description: the PLATFORM axis — not an account role
                  required:
                  - app_role
              required:
              - user
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: delete_user
      summary: Hard-delete a user — irreversible
      description: Their tokens, identities and personal account go too. Refused for
        yourself, and while they solely own a shared account.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{account_slug}/members":
    post:
      operationId: system_add_member
      summary: Add a user to an account, or invite an unknown email
      description: 'The support-access door: works on suspended accounts, and there
        is no grant ceiling — the operator is not a member. An existing user becomes
        a member immediately; an unknown email gets a pending invite.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum: *1
              required:
              - email
              - role
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{account_slug}/members/{id}":
    patch:
      operationId: system_change_member_role
      summary: Change a member's account role from the operator side
      description: The last owner cannot be demoted.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                account_member:
                  type: object
                  properties:
                    role:
                      type: string
                      enum: *1
                      description: 'roles are inclusive: owner ⊃ admin ⊃ member'
                  required:
                  - role
              required:
              - account_member
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: system_remove_member
      summary: Remove a member from the operator side
      description: The last owner cannot be removed.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{account_slug}/tools":
    post:
      operationId: grant_tool
      summary: Enable a tool for an account
      description: The tool appears in the account's navigation and its pages start
        answering immediately.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                tool_key:
                  type: string
                  enum:
                  - amp_events
                  - labs
              required:
              - tool_key
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/accounts/{account_slug}/tools/{tool_key}":
    patch:
      operationId: update_tool_grant
      summary: Update a grant's per-account tool settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                account_tool:
                  type: object
                  properties:
                    settings:
                      type: object
                      properties:
                        free_event_limit:
                          type:
                          - integer
                          - 'null'
                          minimum: 0
                          description: 'Marketplace Events (amp_events) only: account-level
                            default free event limit for the tool''s products; blank/null
                            clears it (platform default applies); a product''s own
                            override still wins'
                        spending_cap_usd:
                          type:
                          - string
                          - 'null'
                          description: 'Labs (labs) only: the account''s AI spending
                            cap in USD, a decimal string (e.g. "5.00") — overrides
                            the platform default of $0.10; blank/null clears back
                            to the default; non-negative, at most $1000'
              required:
              - account_tool
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: revoke_tool
      summary: Disable a tool for an account
      description: The tool's pages 404 for the account immediately. Its data is kept;
        re-granting restores access.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/tool_plans":
    post:
      operationId: create_tool_plan
      summary: Add a plan to a tool's pricing catalog
      description: A plan is offered to accounts only while active AND carrying every
        Stripe price its billing model needs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                tool_plan:
                  type: object
                  properties:
                    tool_key:
                      type: string
                      enum:
                      - amp_events
                      - labs
                    name:
                      type: string
                    billing_model:
                      type: string
                      enum: &2
                      - flat
                      - metered
                      - flat_plus_metered
                      description: flat = fixed recurring price; metered = usage-billed
                        via a Stripe Billing Meter; flat_plus_metered = both
                    stripe_product_id:
                      type: string
                      description: prod_… — authored in the Stripe dashboard
                    stripe_price_ids:
                      type: object
                      properties:
                        flat:
                          type: string
                          description: price_…; blank clears
                        metered:
                          type: string
                          description: price_…; blank clears
                    config:
                      type: object
                      properties:
                        included_events:
                          type: integer
                          description: 'informational: events the flat fee covers'
                        stripe_meter_event_name:
                          type: string
                          description: the Billing Meter's event_name; required before
                            metered usage reports
                    active:
                      type: boolean
                      description: inactive plans stay in the catalog but are never
                        offered
                    position:
                      type: integer
                      description: sort order on the billing page, lowest first
                  required:
                  - tool_key
                  - name
                  - billing_model
              required:
              - tool_plan
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/tool_plans/{id}":
    patch:
      operationId: update_tool_plan
      summary: Update a catalog plan (rename, reprice, retire via active:false)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                tool_plan:
                  type: object
                  properties:
                    tool_key:
                      type: string
                      enum:
                      - amp_events
                      - labs
                    name:
                      type: string
                    billing_model:
                      type: string
                      enum: *2
                      description: flat = fixed recurring price; metered = usage-billed
                        via a Stripe Billing Meter; flat_plus_metered = both
                    stripe_product_id:
                      type: string
                      description: prod_… — authored in the Stripe dashboard
                    stripe_price_ids:
                      type: object
                      properties:
                        flat:
                          type: string
                          description: price_…; blank clears
                        metered:
                          type: string
                          description: price_…; blank clears
                    config:
                      type: object
                      properties:
                        included_events:
                          type: integer
                          description: 'informational: events the flat fee covers'
                        stripe_meter_event_name:
                          type: string
                          description: the Billing Meter's event_name; required before
                            metered usage reports
                    active:
                      type: boolean
                      description: inactive plans stay in the catalog but are never
                        offered
                    position:
                      type: integer
                      description: sort order on the billing page, lowest first
                  required:
                  - tool_key
                  - name
                  - billing_model
              required:
              - tool_plan
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: delete_tool_plan
      summary: Delete a catalog plan
      description: Refused while any subscription points at the plan — deactivate
        instead to retire it.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/settings":
    patch:
      operationId: update_platform_settings
      summary: Update platform-wide settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_setting:
                  type: object
                  properties:
                    allow_registration:
                      type: boolean
                      description: off = invite-only signup
                    amp_events_free_limit:
                      type: integer
                      description: default free event limit; a product can override
                        it
              required:
              - app_setting
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/amp_events/products/{id}":
    patch:
      operationId: system_update_product
      summary: Pause/resume a product's sync, or set its free-limit override
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product:
                  type: object
                  properties:
                    sync_enabled:
                      type: boolean
                    free_event_limit:
                      type:
                      - integer
                      - 'null'
                      description: null/blank = follow the platform default; 0 = pause
                        on the next event
              required:
              - product
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/amp_events/products/{id}/syncs":
    post:
      operationId: system_start_sync
      summary: Start one sync run for any product
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/amp_events/products/{id}/sync_state":
    delete:
      operationId: reset_sync_state
      summary: Delete a product's sync-state snapshot; the next run rebuilds a clean
        baseline
      description: Licences, transactions, events, runs and dead letters are all kept;
        no event is re-sent.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/amp_events/dead_letters/{id}/resolve":
    patch:
      operationId: system_resolve_dead_letter
      summary: Mark a dead letter resolved (forensics kept)
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/amp_events/anomalies/{id}/acknowledge":
    patch:
      operationId: system_acknowledge_anomaly
      summary: Acknowledge a contract anomaly
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/queue/failed_executions/{failed_execution_id}/retries":
    post:
      operationId: queue_retry_failure
      summary: Retry one failed job — it goes back on its queue and runs again
      description: The failure row is deleted and the job's attempt counters are reset.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/queue/retries":
    post:
      operationId: queue_retry_failures
      summary: Retry every failed job the filter selects
      description: Selects by ?job_class=, ?exception_class= and ?q= — the same query
        the failed-jobs list uses, so an agent can read the list, then act on exactly
        what it read. Bounded to 1000 failures per request; repeat the call to work
        through more.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/queue/failed_executions/{id}":
    delete:
      operationId: queue_discard_failure
      summary: Discard one failed job — irreversible
      description: Deletes the job itself, not just the failure record. The work is
        not retried and cannot be recovered.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/queue/failed_executions":
    delete:
      operationId: queue_discard_failures
      summary: Discard every failed job the filter selects — irreversible
      description: Deletes the jobs themselves. The work is not retried and cannot
        be recovered. Selects by ?job_class=, ?exception_class= and ?q= — the same
        query the failed-jobs list uses, so an agent can read the list, then act on
        exactly what it read. Bounded to 1000 failures per request; repeat the call
        to work through more.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/processes/{name}":
    patch:
      operationId: system_labs_set_process_active
      summary: Activate or deactivate a Labs process
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                process:
                  type: object
                  properties:
                    is_active:
                      type: boolean
                      description: inactive processes refuse generation; history and
                        prompts are kept
                  required:
                  - is_active
              required:
              - process
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/processes/{process_name}/revisions":
    post:
      operationId: system_labs_save_revision
      summary: Save new process YAML as an append-only revision
      description: 'Validator errors refuse the save (422) — including raw provider:model
        ids under model:/default_model:, which must be aliases from /system/labs/model_prefs;
        warnings save and are reported back. History is never rewritten: a new revision
        row is appended and the current pointer moves. Roll back by saving an old
        revision''s YAML again.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                revision:
                  type: object
                  properties:
                    process_yaml:
                      type: string
                      description: the full process.yaml; validated before saving
                  required:
                  - process_yaml
              required:
              - revision
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/processes/{process_name}/prompts":
    post:
      operationId: system_labs_append_prompt
      summary: Append a new version of a prompt (editing IS appending)
      description: Old versions are kept forever (deliverable provenance resolves
        against them). The YAML pins name@version refs, so nothing runs the new version
        until a revision re-points to it.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: object
                  properties:
                    name:
                      type: string
                      description: the prompt's name; an existing name gets its next
                        version slot, a new name starts at version one
                    prompt_type:
                      type: string
                      enum:
                      - system
                      - user
                      - evaluation
                      - security
                    description:
                      type: string
                    inputs:
                      type: string
                      description: comma-separated variable names this prompt consumes
                    output:
                      type: string
                      description: the step-output key this prompt fills
                    content:
                      type: string
                      description: the prompt body; {{ variables }} are validated
                        against declared inputs when a revision references it
                  required:
                  - name
                  - prompt_type
                  - content
              required:
              - prompt
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/model_prefs":
    post:
      operationId: system_labs_create_model_pref
      summary: Add a model alias (a role processes can reference)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                model_pref:
                  type: object
                  properties:
                    name:
                      type: string
                      description: lowercase words-and-dashes, no colon
                    model_id:
                      type: string
                      description: exact provider:model id (the colon is required
                        — aliases are colon-less)
                    notes:
                      type: string
                      description: why this mapping; heals overwrite it with their
                        own story
                  required:
                  - name
                  - model_id
              required:
              - model_pref
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/model_prefs/{id}":
    patch:
      operationId: system_labs_update_model_pref
      summary: Repoint a model alias — every referencing process follows on its next
        run
      description: 'The alias name itself is immutable: it is the contract process
        YAML hangs on.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                model_pref:
                  type: object
                  properties:
                    model_id:
                      type: string
                      description: exact provider:model id (the colon is required
                        — aliases are colon-less)
                    notes:
                      type: string
                      description: why this mapping; heals overwrite it with their
                        own story
                  required:
                  - model_id
              required:
              - model_pref
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
    delete:
      operationId: system_labs_delete_model_pref
      summary: Delete a model alias
      description: Refused while any process's current definition still references
        the alias — the alert names them.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/products/{id}":
    delete:
      operationId: system_labs_delete_product
      summary: Delete a lead product — the operator call the account surface lacks
      description: Its deliverables and share links are deleted with it; usage-ledger
        rows are KEPT with the product reference cleared, because spend accounting
        outlives what was spent on.
      requestBody:
        description: No body — an empty request.
        required: false
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
  "/system/labs/products/{product_id}/deliverables":
    post:
      operationId: system_labs_generate_deliverable
      summary: Generate (or regenerate) a deliverable as the operator — skips the
        24h cooldown
      description: 'The operator exemption covers ONLY the cooldown: entitlement,
        suspension and the account''s spending cap still refuse the run. Costs real
        AI spend against the product''s account.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                deliverable:
                  type: object
                  properties:
                    deliverable_type:
                      type: string
                      enum:
                      - product_analysis
                      - business_case
                      - tracking_plan
                      - customer_discovery
                      - engagement_playbook
                      - engagement_playbook_deep
                      - engagement_playbook_from_config
                      - whats_next
                  required:
                  - deliverable_type
              required:
              - deliverable
      responses:
        '200':
          description: OK
        '403':
          description: Read-only token, or not permitted
