openapi: 3.0.0
info:
  title: Public API
  version: 1.0.0
servers:
  - url: 'https://api.gumloop.com/api/v1'
paths:
  /start_pipeline:
    post:
      summary: Start flow run
      description: This endpoint is used to trigger a flow run via API
      operationId: startFlow
      tags:
        - Execution
      requestBody:
        required: true
        description: >
          In addition to the documented top-level fields, you may include any
          additional inputs in the request body.
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: string
                  description: The id for the user initiating the flow.
                project_id:
                  type: string
                  description: (Optional) The id of the project within which the flow is executed.
                saved_item_id:
                  type: string
                  description: The id for the saved flow.
              required:
                - saved_item_id
                - user_id
              additionalProperties:
                description: >
                  Arbitrary input value. The property key is matched by name to
                  an input step in the flow, and the entire body will be forwarded to any webhook input step.
            example:
              user_id: xxxxxxxxxxxxxx
              saved_item_id: xxxxxxxxxxxxxx
              recipient: recipient@gmail.com
              subject: Example of an Email Subject Line
              body: Example of the Text of an Email Body
              topic: weekly update
      responses:
        '200':
          description: Flow started successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  run_id:
                    type: string
                  saved_item_id:
                    type: string
                  workbook_id:
                    type: string
                  url:
                    type: string
        '400':
          description: Bad request (missing or conflicting parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /kill_pipeline:
    post:
      summary: Kill flow run
      description: This endpoint is used to kill a flow run and all its subflow runs.
      operationId: killPipeline
      tags:
        - Execution
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                run_id:
                  type: string
                  description: The ID of the pipeline run to kill.
                user_id:
                  type: string
                  description: The user ID. Required if project_id is not provided.
                project_id:
                  type: string
                  description: The project ID. Required if user_id is not provided.
              required:
                - run_id
      responses:
        '200':
          description: Pipeline killed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  run_id:
                    type: string
                    description: The ID of the killed pipeline run.
        '400':
          description: Bad request (missing run_id)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user does not have permission to kill this run)
        '404':
          description: Pipeline run not found
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /get_pl_run:
    get:
      summary: Retrieve run details
      description: This endpoint can be used to poll for completion and retrieve final flow outputs. Output steps must be used to retrieve outputs.
      operationId: getAutomationRun
      tags:
        - Data Access
      parameters:
        - in: query
          name: run_id
          required: true
          schema:
            type: string
          description: ID of the flow run to retrieve
        - in: query
          name: user_id
          required: false
          schema:
            type: string
          description: The id for the user initiating the flow. Required if project_id is not provided.
        - in: query
          name: project_id
          required: false
          schema:
            type: string
          description: The id of the project within which the flow is executed. Required if user_id is not provided.
      responses:
        '200':
          description: Successful retrieval of flow run details
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_id:
                    type: string
                    description: The id for the user initiating the flow.
                  state:
                    type: string
                    enum: ['RUNNING', 'DONE', 'TERMINATING', 'FAILED', 'TERMINATED', 'QUEUED']
                  outputs:
                    type: object
                    description: JSON object where keys are the `output_name` parameters of your Gumloop output steps and the values are the values that get sent to each step.
                  created_ts:
                    type: string
                    format: date-time
                    description: Timestamp for when the flow was started.
                  finished_ts:
                    type: string
                    format: date-time
                    description: Timestamp for when the flow completed.
                  log:
                    type: array
                    items:
                      type: string
                    description: A list of log entries from your Gumloop flow run.
        '400':
          description: Bad request (missing run_id)
        '404':
          description: Flow run not found
      security:
        - bearerAuth: []

  /list_workbooks:
    get:
      summary: List workbooks and their saved flows
      operationId: listWorkbooks
      tags:
        - Data Access
      parameters:
        - in: query
          name: user_id
          required: false
          schema:
            type: string
          description: The user ID for which to list workbooks. Required if project_id is not provided.
        - in: query
          name: project_id
          required: false
          schema:
            type: string
          description: The project ID for which to list workbooks. Required if user_id is not provided.
      responses:
        '200':
          description: Successful retrieval of workbooks and their saved items
          content:
            application/json:
              schema:
                type: object
                properties:
                  workbooks:
                    type: array
                    items:
                      type: object
                      properties:
                        workbook_id:
                          type: string
                          description: The id of the workbook.
                        name:
                          type: string
                          description: The name of the workbook.
                        description:
                          type: string
                          description: The description of the workbook.
                        created_ts:
                          type: string
                          format: date-time
                          description: Timestamp for when the workbook was created.
                        saved_items:
                          type: array
                          items:
                            type: object
                            properties:
                              saved_item_id:
                                type: string
                                description: The id of the saved flow.
                              name:
                                type: string
                                description: The name of the saved flow.
                              description:
                                type: string
                                description: The description of the saved flow.
                              created_ts:
                                type: string
                                format: date-time
                                description: Timestamp for when the saved flow was created.
                    description: List of workbooks and their associated saved flows
        '400':
          description: Bad request (missing parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /list_saved_items:
    get:
      summary: List saved flows
      operationId: listSavedAutomations
      tags:
        - Data Access
      parameters:
        - in: query
          name: user_id
          required: false
          schema:
            type: string
          description: The user ID to for which to list items. Required if project_id is not provided.
        - in: query
          name: project_id
          required: false
          schema:
            type: string
          description: The project ID for which to list items. Required if user_id is not provided.
      responses:
        '200':
          description: Successful retrieval of saved items
          content:
            application/json:
              schema:
                type: object
                properties:
                  saved_items:
                    type: array
                    items:
                      type: object
                      properties:
                        saved_item_id:
                          type: string
                          description: The id for the saved flow.
                        name:
                          type: string
                          description: The name of the saved flow.
                        description:
                          type: string
                          description: The description of the saved flow.
                        created_ts:
                          type: string
                          format: date-time
                          description: Timestamp for when the flow was started.
                    description: List of saved flows
        '400':
          description: Bad request (missing parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /get_inputs:
    get:
      summary: Retrieve input schema
      operationId: getInputs
      tags:
        - Data Access
      parameters:
        - in: query
          name: saved_item_id
          required: true
          schema:
            type: string
          description: The ID of the saved item for which to retrieve input schemas.
        - in: query
          name: user_id
          required: false
          schema:
            type: string
          description: User ID that created the flow. Required if project_id is not provided.
        - in: query
          name: project_id
          required: false
          schema:
            type: string
          description: Project ID that the flow is under. Required if user_id is not provided.
      responses:
        '200':
          description: Successful retrieval of item input schemas
          content:
            application/json:
              schema:
                type: object
                properties:
                  inputs:
                    type: array
                    items:
                      type: object
                      properties:
                        data_type:
                          type: string
                          enum: [string, file]
                          description: The type of the input, either a 'string' or a 'file'.
                        description:
                          type: string
                          nullable: true
                          description: A description of the input. Can be null if no desecription is given.
                        name:
                          type: string
                          description: The name of the input.
                    description: List of inputs for the saved item.
        '400':
          description: Bad request (missing parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '404':
          description: Saved item not found
      security:
        - bearerAuth: []

  /get_plrun_saved_item_map:
    get:
      summary: Retrieve automation run history
      description: This endpoint retrieves the run history for automations, either by workbook or saved item. Returns the 10 most recent runs.
      operationId: getAutomationRunHistory
      tags:
        - Data Access
      parameters:
        - in: query
          name: workbook_id
          required: false
          schema:
            type: string
          description: The ID of the workbook to retrieve run history for. Required if saved_item_id is not provided.
        - in: query
          name: saved_item_id
          required: false
          schema:
            type: string
          description: The ID of the saved item to retrieve run history for. Required if workbook_id is not provided.
        - in: query
          name: user_id
          required: false
          schema:
            type: string
          description: The user ID. Required if project_id is not provided.
        - in: query
          name: project_id
          required: false
          schema:
            type: string
          description: The project ID. Required if user_id is not provided.
      responses:
        '200':
          description: Successful retrieval of automation run history
          content:
            application/json:
              schema:
                type: object
                description: A map of saved item IDs to their recent run history, with up to 10 runs per saved item
        '400':
          description: Bad request (missing workbook_id or saved_item_id parameter)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '404':
          description: Workbook not found
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /download_file:
    post:
      summary: Download file
      operationId: downloadFile
      tags:
        - File Handling
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                file_name:
                  type: string
                  description: The name of the file to download.
                run_id:
                  type: string
                  description: The ID of the flow run associated with the file.
                saved_item_id:
                  type: string
                  description: The saved item ID associated with the file.
                user_id:
                  type: string
                  description: Optional. The user ID associated with the flow run.
                project_id:
                  type: string
                  description: Optional. The project ID associated with the flow run.
      responses:
        '200':
          description: File downloaded successfully
        '400':
          description: Bad request (missing file_name or other required data)
        '403':
          description: Unauthorized (API key or user verification failed)
        '500':
          description: Internal server error (file download failed)
      security:
        - bearerAuth: []

  /download_files:
    post:
      summary: Download multiple files
      operationId: downloadFiles
      tags:
        - File Handling
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                file_names:
                  type: array
                  items:
                    type: string
                  description: An array of file names to download.
                run_id:
                  type: string
                  description: The ID of the flow run associated with the files.
                user_id:
                  type: string
                  description: The user ID associated with the files. Required if project_id is not provided.
                project_id:
                  type: string
                  description: The project ID associated with the files. Required if user_id is not provided.
                saved_item_id:
                  type: string
                  description: Optional. The saved item ID associated with the files.
      responses:
        '200':
          description: Files downloaded successfully as a zip
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '400':
          description: Bad request (missing file_names or other required data)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error (file download failed)
      security:
        - bearerAuth: []

  /upload_file:
    post:
      summary: Upload file
      operationId: uploadFile
      tags:
        - File Handling
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                file_name:
                  type: string
                  description: The name of the file to be uploaded.
                file_content:
                  type: string
                  format: byte
                  description: Base64 encoded content of the file.
                user_id:
                  type: string
                  description: The user ID associated with the file. Required if project_id is not provided.
                project_id:
                  type: string
                  description: The project ID associated with the file. Required if user_id is not provided.
      responses:
        '200':
          description: File uploaded successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  file_name:
                    type: string
                    description: The name of the uploaded file.
        '400':
          description: Bad request (missing file or required parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error (file upload failed)
      security:
        - bearerAuth: []

  /upload_files:
    post:
      summary: Upload multiple files
      operationId: uploadFiles
      tags:
        - File Handling
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                files:
                  type: array
                  items:
                    type: object
                    properties:
                      file_name:
                        type: string
                        description: The name of the file to be uploaded.
                      file_content:
                        type: string
                        format: byte
                        description: Base64 encoded content of the file.
                user_id:
                  type: string
                  description: The user ID associated with the files. Required if project_id is not provided.
                project_id:
                  type: string
                  description: The project ID associated with the files. Required if user_id is not provided.
      responses:
        '200':
          description: All files uploaded successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  uploaded_files:
                    type: array
                    items:
                      type: string
                    description: Array of uploaded file names.
        '207':
          description: Partial success (some files failed to upload)
          content:
            application/json:
              schema:
                type: object
                properties:
                  partial_success:
                    type: boolean
                  uploaded_files:
                    type: array
                    items:
                      type: string
                    description: Array of successfully uploaded file names.
                  failed_files:
                    type: array
                    items:
                      type: string
                    description: Array of file names that failed to upload.
        '400':
          description: Bad request (missing files or required parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (API key does not match)
        '500':
          description: Internal server error (all file uploads failed)
      security:
        - bearerAuth: []

  /get_audit_logs:
    get:
      summary: Retrieve audit logs
      description: This endpoint retrieves audit logs for all users in an organization for a specified time period.
      operationId: getOrganizationAuditLogs
      tags:
        - Organization
      parameters:
        - in: query
          name: organization_id
          required: true
          schema:
            type: string
          description: The ID of the organization to retrieve audit logs for.
        - in: query
          name: user_id
          required: true
          schema:
            type: string
          description: Your user id -- you must be an organization admin to retrieve organization logs.
        - in: query
          name: start_time
          required: true
          schema:
            type: string
            format: date-time
          description: Start timestamp for log filtering (ISO format).
        - in: query
          name: end_time
          required: true
          schema:
            type: string
            format: date-time
          description: End timestamp for log filtering (ISO format).
        - in: query
          name: event_types
          required: false
          schema:
            type: string
          description: Comma-separated list of event types to filter by (e.g. `user_sign_in,credential_retrieval`). The singular `event_type` param accepts a single value.
        - in: query
          name: user_ids
          required: false
          schema:
            type: string
          description: Comma-separated list of user IDs whose events should be returned.
        - in: query
          name: ip_addresses
          required: false
          schema:
            type: string
          description: Comma-separated list of source IP addresses to filter by. The singular `ip_address` param accepts a single value.
        - in: query
          name: workspace_ids
          required: false
          schema:
            type: string
          description: Comma-separated list of workspace (team) IDs to filter by. The singular `workspace_id` param accepts a single value.
        - in: query
          name: entity_ids
          required: false
          schema:
            type: string
          description: Comma-separated list of entity IDs (agents, workbooks, files) to filter by. The singular `entity_id` param accepts a single value.
        - in: query
          name: page
          required: false
          schema:
            type: integer
            default: 1
          description: Page number for pagination.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 50
          description: Number of records per page.
      responses:
        '200':
          description: Successful retrieval of organization audit logs
          content:
            application/json:
              schema:
                type: object
                properties:
                  audit_logs:
                    type: array
                    items:
                      type: object
                      properties:
                        event_id:
                          type: string
                          description: Unique identifier for the audit log event.
                        timestamp:
                          type: string
                          format: date-time
                          description: When the event occurred.
                        event_type:
                          type: string
                          description: Type of event that was logged.
                        user_id:
                          type: string
                          description: ID of the user who performed the action.
                        details:
                          type: string
                          description: Additional details about the event.
                        source_ip:
                          type: string
                          description: IP address from which the action was performed.
                        user_agent:
                          type: string
                          description: User agent information from the request.
                  total_count:
                    type: integer
                    description: Total number of matching audit logs.
                  page:
                    type: integer
                    description: Current page number.
                  page_size:
                    type: integer
                    description: Number of records per page.
                  total_pages:
                    type: integer
                    description: Total number of pages available.
                  event_types:
                    type: array
                    items:
                      type: string
                    description: Every event type available for filtering in this organization.
        '400':
          description: Bad request (missing required parameters)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin)
        '404':
          description: Organization not found
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /manage_workspace_users:
    post:
      summary: Manage workspace users
      description: This endpoint allows organization administrators to add or remove users from a workspace.
      operationId: manageProjectUsers
      tags:
        - Organization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                organization_id:
                  type: string
                  description: The ID of the organization that the workspace belongs to.
                user_id:
                  type: string
                  description: Your user id -- you must be an organization admin to manage workspace users.
                workspace_id:
                  type: string
                  description: The ID of the workspace to manage users for.
                action:
                  type: string
                  enum: [add, remove]
                  description: The action to perform - either 'add' or 'remove' a user.
                user_email:
                  type: string
                  description: The email address of the target user to add or remove.
                is_admin:
                  type: boolean
                  description: When adding a user, specify whether they should have admin privileges (default is false).
              required:
                - organization_id
                - user_id
                - workspace_id
                - action
                - user_email
      responses:
        '200':
          description: User successfully added to or removed from workspace
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    description: A success message describing the action performed.
        '400':
          description: Bad request (missing required parameters or invalid action)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin or workspace doesn't belong to organization)
        '404':
          description: Organization, workspace, or user not found
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /manage_permission_group_users:
    post:
      summary: Manage custom role users
      description: This endpoint allows organization administrators to add or remove users from a custom role (formerly "permission group"). Adding a user to a role does not remove them from any other role they belong to.
      operationId: managePermissionGroupUsers
      tags:
        - Organization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                organization_id:
                  type: string
                  description: The ID of the organization that the custom role belongs to.
                user_id:
                  type: string
                  description: Your user id -- you must be an organization admin to manage custom role users.
                group_id:
                  type: string
                  description: The ID of the custom role to manage users for.
                action:
                  type: string
                  enum: [add, remove]
                  description: The action to perform - either 'add' or 'remove' a user.
                user_email:
                  type: string
                  description: The email address of the target user to add or remove.
              required:
                - organization_id
                - user_id
                - group_id
                - action
                - user_email
      responses:
        '200':
          description: User successfully added to or removed from the custom role
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    description: A success message describing the action performed.
        '400':
          description: Bad request (missing required parameters or invalid action)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin or custom role doesn't belong to organization)
        '404':
          description: Organization, custom role, or user not found
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /organizations/{organization_id}/roles/credit-limits:
    get:
      summary: List custom role credit limits
      description: >
        This endpoint lists every active custom role in an organization together with its
        monthly credit limit, so external systems can manage credit limits programmatically.
        A `monthly_credit_limit` of `null` means the role sets no limit of its own. The limit
        applies to each member of the role individually; when a user belongs to multiple
        roles, the highest limit across their roles wins.
      operationId: listRoleCreditLimits
      tags:
        - Organization
      parameters:
        - in: path
          name: organization_id
          required: true
          schema:
            type: string
          description: The ID of the organization.
        - in: query
          name: user_id
          required: true
          schema:
            type: string
          description: Your user id -- you must be an organization admin to manage custom role credit limits.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            maximum: 100
          description: Number of roles per page.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque cursor from a previous response's `next_cursor`; omit for the first page.
      responses:
        '200':
          description: Custom roles with their credit limits
          content:
            application/json:
              schema:
                type: object
                properties:
                  roles:
                    type: array
                    items:
                      $ref: '#/components/schemas/RoleCreditLimit'
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor for the next page, or null when there are no more roles.
              example:
                roles:
                  - role_id: aBcDeFgHiJkLmNoPqRsTuV
                    role_name: Engineering
                    is_default: false
                    member_count: 42
                    monthly_credit_limit: 5000
                  - role_id: xYzAbCdEfGhIjKlMnOpQrS
                    role_name: Everyone
                    is_default: true
                    member_count: 310
                    monthly_credit_limit: null
                next_cursor: null
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin or the organization is not on the Enterprise plan)
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /organizations/{organization_id}/roles/{role_id}/credit-limit:
    get:
      summary: Get custom role credit limit
      description: >
        This endpoint returns the monthly credit limit of one custom role.
        A `monthly_credit_limit` of `null` means the role sets no limit of its own.
      operationId: getRoleCreditLimit
      tags:
        - Organization
      parameters:
        - in: path
          name: organization_id
          required: true
          schema:
            type: string
          description: The ID of the organization the custom role belongs to.
        - in: path
          name: role_id
          required: true
          schema:
            type: string
          description: The ID of the custom role (the same ID used as `group_id` by the Manage custom role users endpoint).
        - in: query
          name: user_id
          required: true
          schema:
            type: string
          description: Your user id -- you must be an organization admin to manage custom role credit limits.
      responses:
        '200':
          description: The custom role and its credit limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleCreditLimit'
              example:
                role_id: aBcDeFgHiJkLmNoPqRsTuV
                role_name: Engineering
                is_default: false
                member_count: 42
                monthly_credit_limit: 5000
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin or the organization is not on the Enterprise plan)
        '404':
          description: Custom role not found in this organization
        '500':
          description: Internal server error
      security:
        - bearerAuth: []
    put:
      summary: Set custom role credit limit
      description: >
        This endpoint sets or clears the monthly credit limit of a custom role. The limit
        applies to each member of the role individually and takes effect immediately:
        member allowances are recalculated while preserving credits already used in the
        current billing cycle. Send `"monthly_credit_limit": null` to clear the role-level
        limit so members revert to the organization default. When a user belongs to
        multiple roles, the highest limit across their roles wins. Requests that do not
        change the stored value are no-ops. Changes are recorded in the organization
        audit trail.
      operationId: setRoleCreditLimit
      tags:
        - Organization
      parameters:
        - in: path
          name: organization_id
          required: true
          schema:
            type: string
          description: The ID of the organization the custom role belongs to.
        - in: path
          name: role_id
          required: true
          schema:
            type: string
          description: The ID of the custom role (the same ID used as `group_id` by the Manage custom role users endpoint).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                monthly_credit_limit:
                  type: integer
                  nullable: true
                  minimum: 0
                  maximum: 1000000000
                  description: The monthly credit limit applied to each member of this role, or null to clear the role-level limit.
                user_id:
                  type: string
                  description: Your user id -- you must be an organization admin to manage custom role credit limits.
              required:
                - monthly_credit_limit
                - user_id
            example:
              monthly_credit_limit: 10000
              user_id: xxxxxxxxxxxxxx
      responses:
        '200':
          description: The custom role with its updated credit limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleCreditLimit'
              example:
                role_id: aBcDeFgHiJkLmNoPqRsTuV
                role_name: Engineering
                is_default: false
                member_count: 42
                monthly_credit_limit: 10000
        '400':
          description: Bad request (missing or invalid monthly_credit_limit)
        '401':
          description: Unauthorized (missing or invalid API key)
        '403':
          description: Forbidden (user is not an organization admin or the organization is not on the Enterprise plan)
        '404':
          description: Custom role not found in this organization
        '500':
          description: Internal server error
      security:
        - bearerAuth: []

  /export_data:
    post:
      summary: Export data
      description: |
        This endpoint allows enterprise organization administrators to create and initiate a comprehensive data export for their organization or specific workspaces.

        The export supports six data types:
        - **Workflow data** (`data_type: "workflows"`): Includes workflow runs, workbook details, user information, and other organizational data.
        - **Agent data** (`data_type: "agents"`): Includes agent configurations, metadata, tools, and creator information.
        - **Agent interaction data** (`data_type: "agent_interactions"`): Includes agent interaction data with timestamps, credit costs, trigger types, and message counts.
        - **Credit log data** (`data_type: "credit_logs"`): Includes credit transaction history with charges, balances, categories, and user attribution.
        - **Interaction evaluation data** (`data_type: "interaction_evaluations"`): Includes one row per completed [evaluation](/core-concepts/evaluations) of a chat, with its grade, call outcome, sentiment, and the model that graded it.
        - **Gumstack data** (`data_type: "gumstack"`): Includes Gumstack MCP tool call activity with timestamps, statuses, and latency.

        The available `export_fields` depend on the selected `data_type`. See the field descriptions below for details.

        **Scoping requirement:** For non-credit-log exports, at least one scoping parameter must be provided: `workspace_ids`, `include_all_workspaces`, `include_personal_workspaces`, or `entity_ids`. Requests that omit all scoping parameters will receive a `400` error.

        **Note:** Credit log exports work differently from workflow and agent exports. When `data_type` is `"credit_logs"`, the following parameters are **not applicable** and will be ignored: `export_level`, `workspace_ids`, `include_all_workspaces`, `include_personal_workspaces`, and `entity_ids`. Credit log exports are always scoped to the entire organization. Use `category_filter` to filter by credit log category.
      operationId: exportData
      tags:
        - Organization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: string
                  description: The ID of the user requesting the export.
                data_type:
                  type: string
                  enum:
                    - workflows
                    - agents
                    - agent_interactions
                    - credit_logs
                    - interaction_evaluations
                    - gumstack
                  default: workflows
                  description: |
                    The type of data to export. Use `"workflows"` to export workflow run data, `"agents"` to export agent configuration data, `"agent_interactions"` to export agent interaction data, `"credit_logs"` to export credit transaction history, `"interaction_evaluations"` to export completed chat evaluations, or `"gumstack"` to export Gumstack tool call activity. Defaults to `"workflows"`. Audit-log exports are available in the Gumloop app only, not through this endpoint.
                category_filter:
                  type: string
                  description: |
                    **Only applicable when `data_type` is `"credit_logs"`.** Optional filter to export only credit logs matching a specific category (e.g., `"PIPELINE_RUN"`, `"AGENT_RUN"`, `"CUSTOM_NODE_RD"`, `"EXTERNAL_GUMCP_CALL"`). When omitted, all categories are included.
                export_level:
                  type: string
                  enum:
                    - workspace
                    - organization
                  default: organization
                  description: |
                    The scope of the export. Use `"organization"` to export across the entire organization, or `"workspace"` to export from a single workspace (requires exactly one ID in `workspace_ids`). Defaults to `"organization"`. **Not applicable when `data_type` is `"credit_logs"`.**
                export_fields:
                  type: array
                  items:
                    type: string
                  description: |
                    Array of fields to include in the export. The available fields depend on the `data_type`.

                    **Workflow fields** (when `data_type` is `"workflows"`):
                    - `workbook_id` - The workbook identifier
                    - `workbook_name` - The workbook name
                    - `workbook_created_ts` - Workbook creation timestamp
                    - `user_id` - The user identifier
                    - `user_email` - The user's email address
                    - `workspace_id` - The workspace identifier
                    - `workspace_name` - The workspace name
                    - `run_id` - The flow run identifier
                    - `credit_cost` - Credits consumed by the run
                    - `pl_run_created_ts` - Flow run creation timestamp
                    - `pl_run_finished_ts` - Flow run completion timestamp
                    - `pipeline` - The full pipeline configuration (JSON)

                    **Agent fields** (when `data_type` is `"agents"`):
                    - `agent_id` - The agent identifier
                    - `agent_name` - The agent name
                    - `agent_description` - The agent description
                    - `agent_model` - The model used by the agent
                    - `agent_system_prompt` - The agent's system prompt
                    - `agent_created_ts` - Agent creation timestamp
                    - `agent_tools` - Tools configured for the agent (JSON)
                    - `agent_metadata` - Additional agent metadata (JSON)
                    - `agent_evaluations_enabled` - Whether the agent's own Evaluations are turned on (`true`/`false`)
                    - `creator_email` - Email of the user who created the agent
                    - `workspace_id` - The workspace identifier
                    - `workspace_name` - The workspace name
                    - `folder_id` - The identifier of the folder containing the agent (empty when the agent is not in a folder)
                    - `folder_name` - The name of the folder containing the agent (empty when the agent is not in a folder)

                    **Agent interaction fields** (when `data_type` is `"agent_interactions"`):
                    - `interaction_id` - Unique identifier for the chat session
                    - `agent_id` - The agent identifier
                    - `agent_name` - The agent name
                    - `interaction_type` - Type of interaction (e.g., chat, slack, api, triggered)
                    - `interaction_name` - Display name of the chat session
                    - `trigger_type` - For triggered interactions, the specific trigger type (e.g., time_based, polling_new_record_salesforce). Null for non-triggered interactions.
                    - `interaction_created_ts` - Chat session creation timestamp
                    - `user_email` - Email of the user who initiated the chat
                    - `credit_cost` - Total credits consumed (LLM + tool + flow)
                    - `llm_credit_cost` - Credits consumed by LLM calls only
                    - `tool_credit_cost` - Credits consumed by tool calls
                    - `flow_credit_cost` - Credits consumed by pipeline runs
                    - `message_count` - Number of messages in the conversation
                    - `workspace_id` - The workspace identifier
                    - `workspace_name` - The workspace name

                    **Interaction evaluation fields** (when `data_type` is `"interaction_evaluations"`):
                    - `evaluation_id` - Unique identifier for the evaluation row
                    - `interaction_id` - The chat session that was evaluated (one chat can have several evaluation rows)
                    - `agent_id` - The identifier of the agent that was evaluated
                    - `organization_evaluation_id` - The organization-level evaluation this row belongs to (empty for agent-level rubrics)
                    - `evaluation_created_ts` - When the evaluation reached its final state
                    - `status` - The evaluation's state
                    - `grade` - The grade the evaluation produced
                    - `call_outcome` - The outcome the evaluation recorded for the conversation
                    - `sentiment` - The sentiment the evaluation recorded
                    - `error_code` - Error code when the evaluation could not complete
                    - `summary` - The evaluation's written summary
                    - `evaluation_model` - The model that ran the evaluation
                    - `credit_cost` - Credits consumed by the evaluation
                    - `user_email` - Email of the user whose chat was evaluated

                    **Credit log fields** (when `data_type` is `"credit_logs"`):
                    - `user_email` - Email of the user associated with the credit log entry
                    - `permission_group_id` - Custom role ID(s) the user belongs to (semicolon-separated if multiple) (disabled by default)
                    - `permission_group_name` - Custom role name(s) the user belongs to (semicolon-separated if multiple) (disabled by default)
                    - `timestamp` - When the credit transaction occurred
                    - `category` - The category of the credit log (e.g., PIPELINE_RUN, AGENT_RUN)
                    - `type` - The specific type of credit charge
                    - `name` - Display name of the credit log entry
                    - `amount` - Number of credits charged or adjusted
                    - `balance` - Credit balance after the transaction
                    - `log_id` - Unique identifier for the credit log entry
                    - `correlation_id` - Join key to the related run or interaction (disabled by default)
                    - `balance_scope` - Whether the balance is organization- or user-scoped (disabled by default)
                    - `project_id` - The project identifier (disabled by default)

                    Not all combinations of selected fields are guaranteed to produce data for every row.
                start_date:
                  type: string
                  format: date-time
                  description: Start date for the export in ISO 8601 format (e.g., `2025-01-01T00:00:00Z`).
                end_date:
                  type: string
                  format: date-time
                  description: End date for the export in ISO 8601 format (e.g., `2025-12-31T23:59:59Z`).
                include_all_workspaces:
                  type: boolean
                  default: false
                  description: Whether to include all workspaces in the organization. When `true`, also sets `include_personal_workspaces` to `true`. **Not applicable when `data_type` is `"credit_logs"`.**
                include_personal_workspaces:
                  type: boolean
                  default: false
                  description: Whether to include personal workspaces in the export. Ignored if `include_all_workspaces` is `true`. **Not applicable when `data_type` is `"credit_logs"`.**
                workspace_ids:
                  type: array
                  items:
                    type: string
                  description: An optional array of workspace IDs to include in the export. When `export_level` is `"workspace"`, exactly one workspace ID is required. Ignored if `include_all_workspaces` is `true`. **Not applicable when `data_type` is `"credit_logs"`.**
                entity_ids:
                  type: array
                  items:
                    type: string
                  description: |
                    An optional array of specific entity IDs to filter the export. For workflow exports (`data_type: "workflows"`), these are workbook IDs. For agent exports (`data_type: "agents"`) and agent interaction exports (`data_type: "agent_interactions"`), these are agent IDs. When provided, only data for the specified entities will be included. **Not applicable when `data_type` is `"credit_logs"`.**
              required:
                - user_id
                - export_fields
                - start_date
                - end_date
      responses:
        '200':
          description: Data export successfully initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates whether the export was successfully initiated.
                  data_export_id:
                    type: string
                    description: Unique identifier for the data export job. Use this with the [Get export status](/api-reference/organization/export-status) endpoint to check progress and download the completed export.
                  state:
                    type: string
                    enum:
                      - REQUESTED
                    description: The initial state of the export job. Will be `REQUESTED` upon creation.
                  created_ts:
                    type: string
                    format: date-time
                    description: Timestamp when the export was created (ISO 8601).
        '400':
          description: Bad request — missing required parameters, invalid date format, invalid `export_level` value, or missing scoping parameters (for non-credit-log exports, at least one of `workspace_ids`, `include_all_workspaces`, `include_personal_workspaces`, or `entity_ids` is required).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — insufficient permissions or the organization is not on the enterprise tier.
        '404':
          description: Organization or workspace not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /export_status:
    get:
      summary: Get data export status
      description: |
        This endpoint retrieves the status of a data export job and optionally downloads the export file (as CSV) if the export has completed successfully.

        Use the `data_export_id` returned by the [Export data](/api-reference/organization/export-data) endpoint to check progress.
      operationId: exportStatus
      tags:
        - Organization
      parameters:
        - in: query
          name: user_id
          required: true
          schema:
            type: string
          description: The ID of the user requesting the export status.
        - in: query
          name: data_export_id
          required: true
          schema:
            type: string
          description: The unique identifier of the data export job to check (returned by the Export data endpoint).
        - in: query
          name: download
          required: false
          schema:
            type: boolean
            default: false
          description: |
            Set to `true` to download the export file directly when the export is completed. When `true` and the export state is `COMPLETED`, the response will be a CSV file download instead of JSON.
      responses:
        '200':
          description: |
            Export status retrieved successfully. If `download=true` and the export state is `COMPLETED`, the response body will be the CSV file.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data_export_id:
                    type: string
                    description: The unique identifier of the data export job.
                  state:
                    type: string
                    enum:
                      - REQUESTED
                      - IN_PROGRESS
                      - COMPLETED
                      - FAILED
                    description: |
                      Current state of the export job:
                      - `REQUESTED` — The export has been created and is queued for processing.
                      - `IN_PROGRESS` — The export is currently being processed.
                      - `COMPLETED` — The export finished successfully and is available for download.
                      - `FAILED` — The export encountered an error.
                  created_ts:
                    type: string
                    format: date-time
                    description: Timestamp when the export was created (ISO 8601).
                  finished_ts:
                    type: string
                    format: date-time
                    description: Timestamp when the export finished (ISO 8601). Only present when the state is `COMPLETED` or `FAILED`.
            text/csv:
              schema:
                type: string
                format: binary
                description: CSV file content returned when `download=true` and the export state is `COMPLETED`.
        '400':
          description: Bad request — missing `data_export_id` parameter.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — insufficient permissions, the export does not belong to the requesting user, or the organization is not on the enterprise tier.
        '404':
          description: Data export not found, or the export file is no longer available.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /agents:
    get:
      summary: List agents
      description: List agents the caller has access to. Filter by team, search by name, or narrow to agents that use a specific tool or trigger. Results can be sorted with `sort_order` and paginated by sending `page_size` and/or `cursor`.
      operationId: listAgents
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents?team_id=YOUR_TEAM_ID&search=research' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.list(team_id="YOUR_TEAM_ID", search="research")
            for agent in response.agents:
                print(agent.id, agent.name)
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the listing to a single team. When omitted, returns agents owned by the authenticated user.
        - in: query
          name: search
          required: false
          schema:
            type: string
          description: Case-insensitive substring match against the agent name.
        - in: query
          name: creator
          required: false
          schema:
            type: string
          description: Filter to agents created by this user ID.
        - in: query
          name: has_triggers
          required: false
          schema:
            type: boolean
          description: When `true`, only returns agents that have at least one active trigger configured.
        - in: query
          name: tool
          required: false
          schema:
            type: string
          description: Filter to agents that use the specified MCP server as a tool.
        - in: query
          name: flow
          required: false
          schema:
            type: string
          description: Filter to agents that use the specified saved flow as a tool.
        - in: query
          name: sort_order
          required: false
          schema:
            type: string
            enum: [newest, oldest, name_asc, name_desc]
            default: newest
          description: Sort order for the listing. Defaults to newest first.
        - in: query
          name: include_last_used
          required: false
          schema:
            type: boolean
          description: When `true`, populates `last_used_at` on each agent with the timestamp of its most recent session.
        - in: query
          name: include_last_updated
          required: false
          schema:
            type: boolean
          description: When `true`, populates `last_updated_at` on each agent with the timestamp of its most recent configuration change.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of agents per page. Sending `page_size` or `cursor` opts into cursor pagination; requests that send neither return the full list.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque cursor from a previous response's `next_cursor`. Pass it to fetch the next page.
      responses:
        '200':
          description: Agents matching the provided filters.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agents:
                    type: array
                    items:
                      $ref: '#/components/schemas/Agent'
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor for the next page. Present only on paginated requests; `null` when there are no more results.
                    example: null
              examples:
                multiple:
                  summary: Multiple agents
                  value:
                    agents:
                      - id: "abc123DEFghiJKL"
                        name: "Sales research agent"
                        description: "Researches accounts and drafts outreach"
                        team_id: "team_4f8c92ab"
                        is_active: true
                        model_name: "anthropic/claude-sonnet-4"
                        system_prompt: "You are a B2B sales research assistant."
                        tools: []
                        resources: []
                        metadata: {}
                        folder_id: null
                        created_at: "2026-05-15T14:32:00Z"
                        active_trigger_count: 2
                      - id: "xYz789AbCdEfGhI"
                        name: "Support triage"
                        description: "Triages incoming support tickets"
                        team_id: "team_4f8c92ab"
                        is_active: true
                        model_name: "openai/gpt-5"
                        system_prompt: null
                        tools: []
                        resources: []
                        metadata: {}
                        folder_id: null
                        created_at: "2026-04-02T09:11:00Z"
                        active_trigger_count: 0
                    next_cursor: null
                empty:
                  summary: No matches
                  value:
                    agents: []
                    next_cursor: null
        '400':
          description: Bad request — `page_size` outside 1–100 (`invalid_page_size`), unknown `sort_order` (`invalid_sort`), or a malformed `cursor` (`invalid_cursor`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have agent access on the requested team.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    post:
      summary: Create agent
      description: Create a new agent. The authenticated caller must have permission to create agents on the target team.
      operationId: createAgent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/agents' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "name": "Sales research agent",
                "model_name": "anthropic/claude-sonnet-4",
                "description": "Researches accounts and drafts outreach",
                "system_prompt": "You are a B2B sales research assistant.",
                "team_id": "team_4f8c92ab"
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.create(
                name="Sales research agent",
                model_name="anthropic/claude-sonnet-4",
                description="Researches accounts and drafts outreach",
                system_prompt="You are a B2B sales research assistant.",
                team_id="team_4f8c92ab",
            )
            print(response.agent.id)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              additionalProperties: false
              properties:
                name:
                  type: string
                  description: Display name for the agent.
                  example: "Sales research agent"
                model_name:
                  type: string
                  nullable: true
                  description: ID of the LLM the agent runs on. Use `GET /models` to discover valid values. Omit for the organization's default agent model.
                  example: "anthropic/claude-sonnet-4"
                description:
                  type: string
                  nullable: true
                  example: "Researches accounts and drafts outreach"
                system_prompt:
                  type: string
                  nullable: true
                  example: "You are a B2B sales research assistant."
                tools:
                  type: array
                  description: Connectors and native abilities to start with. The platform's default native abilities are added behind whatever you send. Prefer `PUT /agents/{agent_id}/mcp-servers/{server_id}` and `PATCH /agents/{agent_id}/abilities` after creation.
                  items:
                    $ref: '#/components/schemas/AgentTool'
                  default: []
                resources:
                  type: array
                  description: Resources attached to the agent.
                  items:
                    type: object
                  default: []
                skill_ids:
                  type: array
                  nullable: true
                  description: |
                    IDs of skills to attach to the agent. Attachment happens inside the create transaction, so an invalid ID fails the whole request (no orphaned agent). Omit to attach none. The caller must hold `INVOKE` on each skill. After creation, manage skills with `PATCH /agents/{agent_id}/skills`.
                  items:
                    type: string
                  example: ["skill_2b9c", "skill_7f1a"]
                knowledge_sources:
                  type: array
                  nullable: true
                  description: Brain sources to attach, with an optional scope each. Attached inside the create transaction, like `skill_ids`. Omit `config` for the whole source.
                  items:
                    type: object
                    required: [connector_id]
                    properties:
                      connector_id:
                        type: string
                      config:
                        $ref: '#/components/schemas/KnowledgeSourceScope'
                  example:
                    - connector_id: "PFqdAMir8PA2Xc6qcszSN9"
                    - connector_id: "Zb1vLq2PwR8TyU3nM6kJcA"
                      config:
                        mode: include_only
                        inclusions:
                          - type: container
                            id: "folder_91ab"
                metadata:
                  allOf:
                    - $ref: '#/components/schemas/AgentMetadata'
                  nullable: true
                  description: Agent settings. Omitted sections take the organization's agent defaults.
                folder_id:
                  type: string
                  nullable: true
                  description: ID of the folder to place the agent in.
                  example: "folder_91ab"
                is_active:
                  type: boolean
                  description: |
                    Whether the agent is active. Defaults to `true`.

                    Setting this to `false` retires the agent: it disappears from `GET /agents`, and `GET`/`PATCH /agents/{agent_id}` return `404`, so it cannot be reactivated through the API. This is not a pause switch — to stop an agent from running while keeping it reachable, disable its triggers instead.
                  default: true
                agent_id:
                  type: string
                  nullable: true
                  description: Optional caller-supplied agent ID. When omitted, the server generates one.
                team_id:
                  type: string
                  nullable: true
                  description: ID of the team to create the agent under. When omitted, the agent is owned by the authenticated user.
                  example: "team_4f8c92ab"
            examples:
              minimal:
                summary: Minimal create
                value:
                  name: "Sales research agent"
                  model_name: "anthropic/claude-sonnet-4"
              full:
                summary: Full create
                value:
                  name: "Sales research agent"
                  model_name: "anthropic/claude-sonnet-4"
                  description: "Researches accounts and drafts outreach"
                  system_prompt: "You are a B2B sales research assistant."
                  tools: []
                  resources: []
                  skill_ids: ["skill_2b9c", "skill_7f1a"]
                  metadata: {}
                  folder_id: "folder_91ab"
                  is_active: true
                  team_id: "team_4f8c92ab"
      responses:
        '201':
          description: Agent created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent:
                    $ref: '#/components/schemas/Agent'
              examples:
                created:
                  summary: Newly created agent
                  value:
                    agent:
                      id: "abc123DEFghiJKL"
                      name: "Sales research agent"
                      description: "Researches accounts and drafts outreach"
                      team_id: "team_4f8c92ab"
                      is_active: true
                      tools: []
                      metadata: {}
                      model_name: "anthropic/claude-sonnet-4"
                      system_prompt: "You are a B2B sales research assistant."
                      resources: []
                      skill_ids: ["skill_2b9c", "skill_7f1a"]
                      folder_id: "folder_91ab"
                      type: null
                      created_at: "2026-05-15T14:32:00Z"
                      active_trigger_count: null
                      creator:
                        id: "user_8c2a1b"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
        '400':
          description: Invalid request body.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have permission to create agents on the requested team.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /agents/{agent_id}:
    get:
      summary: Retrieve agent
      description: |
        Retrieve an agent with its whole configuration: settings, tools, native abilities, skills, knowledge sources, triggers and the current `version`. Sections the caller's agent role hides are omitted.

        `agent_id` also accepts the reserved aliases `gumball` (your personal Gumball agent) and `analytics` (your analytics agent). They resolve to your own copy of the platform agent and answer reads and session endpoints; configuration writes on them return `400 agent_not_customizable`.
      operationId: retrieveAgent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.retrieve("abc123DEFghiJKL")
            print(response.agent.name)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent to retrieve. Also accepts the reserved aliases `gumball` and `analytics`.
      responses:
        '200':
          description: The requested agent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent:
                    $ref: '#/components/schemas/Agent'
              examples:
                single:
                  summary: Single agent
                  value:
                    agent:
                      id: "abc123DEFghiJKL"
                      name: "Sales research agent"
                      description: "Researches accounts and drafts outreach"
                      team_id: "team_4f8c92ab"
                      is_active: true
                      tools: []
                      metadata: {}
                      model_name: "anthropic/claude-sonnet-4"
                      system_prompt: "You are a B2B sales research assistant."
                      resources: []
                      skill_ids: ["skill_2b9c", "skill_7f1a"]
                      folder_id: "folder_91ab"
                      type: null
                      created_at: "2026-05-15T14:32:00Z"
                      active_trigger_count: null
                      creator:
                        id: "user_8c2a1b"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access to this agent.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    patch:
      summary: Update agent
      description: 'Update an agent''s settings. Only fields in the body change; `metadata` merges section by
        section (objects merge, arrays and scalars replace), except `metadata.model_settings`, which replaces
        the saved object whole. Send every model-settings field you want to keep.


        Collections have their own endpoints and one item changes per call: [skills](/api-reference/agents/update-agent-skills),
        [connectors](/api-reference/agents/attach-agent-mcp-server), [knowledge sources](/api-reference/agents/attach-agent-knowledge-source),
        [subagents](/api-reference/agents/attach-agent-subagent), [triggers](/api-reference/agents/create-agent-trigger)
        and [abilities](/api-reference/agents/update-agent-abilities). `tools` is still accepted and replaces
        the whole list; `metadata.subagent.allowed_gummie_ids` is ignored here.


        Send `version` from your last read to make the update conditional: if anyone saved the agent in between,
        the response is `409 agent_version_conflict` with the current version. Omit it to let the last write
        win.


        Platform agents (`gumball`, `analytics`) answer `400 agent_not_customizable`.


        **`is_active: false` is not a pause switch.** It retires the agent: the agent disappears from `GET /agents`,
        and both `GET` and `PATCH /agents/{agent_id}` return `404` afterwards, so you cannot set it back to
        `true` through the API. To stop an agent from running on its own while keeping it fully reachable, disable
        its [triggers](/core-concepts/agent_triggers#managing-active-triggers) instead. To remove it, use `DELETE
        /agents/{agent_id}`.

        '
      operationId: updateAgent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "description": "Researches enterprise accounts and drafts outreach",
                "is_active": true
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.update(
                "abc123DEFghiJKL",
                description="Researches enterprise accounts and drafts outreach",
                is_active=True,
            )
            print(response.agent.description)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent to update. Also accepts the reserved aliases `gumball` and `analytics`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                name:
                  type: string
                  nullable: true
                model_name:
                  type: string
                  nullable: true
                  description: ID of the LLM the agent runs on. Use `GET /models` to discover valid values.
                  example: "anthropic/claude-sonnet-4"
                description:
                  type: string
                  nullable: true
                  example: "Researches enterprise accounts and drafts outreach"
                system_prompt:
                  type: string
                  nullable: true
                tools:
                  type: array
                  nullable: true
                  description: When provided, replaces the agent's whole tool list. Prefer the abilities and connector endpoints.
                  items:
                    $ref: '#/components/schemas/AgentTool'
                resources:
                  type: array
                  nullable: true
                  description: When provided, replaces the agent's resource list.
                  items:
                    type: object
                metadata:
                  allOf:
                    - $ref: '#/components/schemas/AgentMetadata'
                  nullable: true
                is_active:
                  type: boolean
                  nullable: true
                  description: |
                    Setting this to `false` retires the agent: it disappears from `GET /agents`, and `GET`/`PATCH /agents/{agent_id}` return `404`, so it cannot be reactivated through the API. This is not a pause switch — to stop an agent from running while keeping it reachable, disable its triggers instead.
                team_id:
                  type: string
                  nullable: true
                  description: When provided, transfers ownership of the agent to this team.
                version:
                  type: integer
                  minimum: 1
                  nullable: true
                  description: The `version` you last read. The update is refused with `409 agent_version_conflict` if the agent changed since.
                  example: 4
            examples:
              partial:
                summary: Partial update
                value:
                  description: "Researches enterprise accounts and drafts outreach"
                  is_active: true
              conditional:
                summary: Settings change guarded by version
                value:
                  version: 4
                  metadata:
                    fallback:
                      enabled: true
                    max_steps: 30
      responses:
        '200':
          description: The updated agent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent:
                    $ref: '#/components/schemas/Agent'
              examples:
                updated:
                  summary: Updated agent
                  value:
                    agent:
                      id: "abc123DEFghiJKL"
                      name: "Sales research agent"
                      description: "Researches enterprise accounts and drafts outreach"
                      team_id: "team_4f8c92ab"
                      is_active: true
                      tools: []
                      metadata: {}
                      model_name: "anthropic/claude-sonnet-4"
                      system_prompt: "You are a B2B sales research assistant."
                      resources: []
                      folder_id: "folder_91ab"
                      type: null
                      created_at: "2026-05-15T14:32:00Z"
                      active_trigger_count: null
                      creator:
                        id: "user_8c2a1b"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
        '400':
          description: Invalid request body.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have permission to update this agent.
        '404':
          description: Agent not found.
        '409':
          description: '`agent_version_conflict` — the agent changed since the `version` you sent. Re-read and retry.'
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    delete:
      summary: Delete agent
      description: |
        Delete a custom agent. The agent disappears from every list and its triggers stop. Platform agents (`gumball`, `analytics`) cannot be deleted.

        Team API keys are not accepted on this endpoint.
      operationId: deleteAgent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.agents.delete("abc123DEFghiJKL")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
      responses:
        '200':
          description: The delete result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  deleted:
                    type: boolean
                    example: true
        '400':
          description: '`agent_not_customizable` — platform agents cannot be deleted.'
        '401':
          description: Unauthorized — missing or invalid API key, or a team API key.
        '403':
          description: Forbidden — the caller does not have delete access to this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /chat/completions:
    post:
      summary: Create chat completion
      servers:
        - url: 'https://ws.gumloop.com/api/v1'
      description: |
        Run one chat completion on any model Gumloop supports (Anthropic, OpenAI, Google Gemini, OpenRouter
        routes). The request and response use the OpenRouter chat completions schema, which extends
        OpenAI's. An OpenAI-style client works unchanged. OpenRouter's `reasoning`, `plugins`, `provider`,
        `modalities` and `image_config` fields are accepted too.

        Image-generation models (`gpt-image-*`, `gemini-*-image-preview`) run when `modalities` includes
        `"image"` and return image attachments on `choices[0].message.images`. Decision models
        (`typesafe/jev-*`) are not chat models. Send those to `POST /decisions`.

        ### Host

        Chat completions are served from the streaming host, `POST https://ws.gumloop.com/api/v1/chat/completions`,
        for both `stream: true` and `stream: false`. `api.gumloop.com` does not serve this endpoint. The Python SDK
        routes there automatically.

        With `stream: true` the response is `text/event-stream` (Server-Sent Events). Each event carries one
        `chat.completion.chunk` and the stream ends with `data: [DONE]`. `client.chat.completions.create(..., stream=True)`
        yields parsed `ChatStreamChunk` objects.

        ### Tool calls, images, and `tool_choice`

        Send messages in the OpenAI shape and Gumloop translates them for the model's provider (Anthropic, OpenAI, and Google Gemini). Models served through OpenRouter and other OpenAI-compatible providers receive the messages as sent.

        - **Tool-result turns**: after the model replies with `finish_reason: "tool_calls"`, append its assistant message (with `tool_calls`) and one `{"role": "tool", "tool_call_id": ..., "content": ...}` message per call, then send the conversation again. Every tool call needs a matching tool message, and every tool message must match a tool call in an earlier assistant message.
        - **Images**: user messages accept `image_url` content parts alongside `text` parts. The URL can be an `http(s)` URL or a base64 data URL (`data:image/png;base64,...`). Images must be JPEG, PNG, GIF, or WebP and at most 20 MB. Redirects are not followed when downloading an image.
        - **`tool_choice`**: `"auto"` (the default when `tools` are sent), `"none"`, `"required"`, or `{"type": "function", "function": {"name": "..."}}` to force one tool.
        - **`developer` messages** are treated like `system` messages.

        ```json
        {
          "model": "claude-sonnet-4-5",
          "tools": [{"type": "function", "function": {"name": "get_weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}}}],
          "messages": [
            {"role": "user", "content": [
              {"type": "text", "text": "What's the weather where this photo was taken?"},
              {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
            ]},
            {"role": "assistant", "content": null, "tool_calls": [
              {"id": "call_1", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Ottawa\"}"}}
            ]},
            {"role": "tool", "tool_call_id": "call_1", "content": "12°C and sunny"}
          ]
        }
        ```

        A request that can't be translated returns `400 invalid_request` with `param` set to the field at fault (for example `messages[3].tool_call_id`). When the provider itself rejects the request (HTTP 400, 404, 413, or 422), the error message relays the provider's reason, prefixed with `The provider rejected the request:`.

        ### Billing

        Each completion charges the caller's credit balance based on token usage (with cache-token semantics per provider) plus a flat 30-credit fee for image-gen calls. Users who configure their own provider API key get a 50% discount.
      operationId: createChatCompletion
      tags:
        - Chat completions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://ws.gumloop.com/api/v1/chat/completions' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "model": "claude-sonnet-4-5",
                "messages": [{"role": "user", "content": "Capital of Canada?"}]
              }'
        - lang: python
          label: Python (unary)
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            result = client.chat.completions.create(
                model="claude-sonnet-4-5",
                messages=[{"role": "user", "content": "Capital of Canada?"}],
            )
            print(result.choices[0].message.content)
        - lang: python
          label: Python (streaming)
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            for chunk in client.chat.completions.create(
                model="claude-sonnet-4-5",
                messages=[{"role": "user", "content": "Write a haiku about Toronto."}],
                stream=True,
            ):
                delta = chunk.choices[0].delta
                if delta.content:
                    print(delta.content, end="", flush=True)
        - lang: python
          label: Python (structured output)
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            result = client.chat.completions.create(
                model="claude-sonnet-4-5",
                messages=[{"role": "user", "content": "Return JSON with the capital of Canada."}],
                response_format={
                    "type": "json_schema",
                    "json_schema": {
                        "name": "answer",
                        "strict": True,
                        "schema": {
                            "type": "object",
                            "properties": {"capital": {"type": "string"}},
                            "required": ["capital"],
                            "additionalProperties": False,
                        },
                    },
                },
            )
            print(result.choices[0].message.content)
        - lang: python
          label: Python (image generation)
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            result = client.chat.completions.create(
                model="gpt-image-1.5",
                messages=[{"role": "user", "content": "A red maple leaf on white"}],
                modalities=["image", "text"],
                image_config={"size": "1024x1024"},
            )
            for image in result.choices[0].message.images:
                print(image.image_url.url[:64], "...")
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - model
                - messages
              properties:
                model:
                  type: string
                  description: Model slug. Use the `id` from `GET /models` or one of Gumloop's preset routes.
                  example: "claude-sonnet-4-5"
                messages:
                  type: array
                  description: Conversation history. Roles `system`, `developer`, `user`, `assistant`, and `tool`. User messages accept multipart `content` with `text` and `image_url` parts. Assistant messages can carry `tool_calls`; answer each one with a `tool` message whose `tool_call_id` matches the call's `id`.
                  items:
                    type: object
                  example:
                    - role: "user"
                      content: "Capital of Canada?"
                stream:
                  type: boolean
                  default: false
                  description: "When `true`, the response is `text/event-stream` carrying one `chat.completion.chunk` per delta and terminating with `data: [DONE]`. Otherwise a unary JSON `chat.completion` is returned."
                temperature:
                  type: number
                  description: Sampling temperature.
                max_completion_tokens:
                  type: integer
                  description: Cap on completion tokens. Replaces the deprecated `max_tokens` field.
                modalities:
                  type: array
                  items:
                    type: string
                    enum: ["text", "image"]
                  description: Output modalities. Include `"image"` to route to an image-generation model.
                image_config:
                  type: object
                  description: "Image-generation parameters (size, quality, aspect_ratio, background, output_format, partial_images). Optional. Image-generation models accept either `modalities: [\"image\"]` or `image_config` (or both); chat models ignore this field."
                response_format:
                  type: object
                  description: |
                    Constrain the response. `{type: "json_object"}` returns a JSON object; `{type: "json_schema", json_schema: {name, strict, schema}}` returns JSON matching the supplied schema.
                tools:
                  type: array
                  description: "OpenAI-shape tool definitions (`{type: \"function\", function: {name, description, parameters}}`). Pass `tool_choice` to constrain selection."
                tool_choice:
                  oneOf:
                    - type: string
                    - type: object
                  description: '`"auto"` lets the model choose and is the default when `tools` are sent. `"none"` disables tool calls, `"required"` forces a tool call, and `{"type": "function", "function": {"name": "..."}}` forces a specific tool.'
                  example: "auto"
                provider:
                  type: object
                  description: OpenRouter provider routing config. Caller fields like `sort` and `order` are honored; ZDR/data_collection policy is server-enforced.
            example:
              model: "claude-sonnet-4-5"
              messages:
                - role: "user"
                  content: "Capital of Canada?"
      responses:
        '200':
          description: |
            Chat completion. When `stream` is `false` (or omitted), the response is the `chat.completion` envelope below. When `stream: true`, the response is `text/event-stream`; each event carries one `chat.completion.chunk` and the terminator is `data: [DONE]`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: "chatcmpl-9a3f8c2b14d6e7"
                  object:
                    type: string
                    example: "chat.completion"
                  created:
                    type: integer
                    example: 1747315920
                  model:
                    type: string
                    example: "claude-sonnet-4-5"
                  choices:
                    type: array
                    items:
                      type: object
                      properties:
                        index:
                          type: integer
                          example: 0
                        finish_reason:
                          type: string
                          enum: [stop, length, tool_calls, content_filter, error]
                          example: "stop"
                        message:
                          type: object
                          properties:
                            role:
                              type: string
                              example: "assistant"
                            content:
                              type: string
                              example: "Ottawa."
                            tool_calls:
                              type: array
                              items:
                                type: object
                            reasoning:
                              type: string
                              description: Reasoning trace (Anthropic thinking blocks, Gemini thoughts, OpenAI o-series).
                            images:
                              type: array
                              description: Image attachments (data URLs). Populated only when an image-gen model produced output.
                              items:
                                type: object
                  usage:
                    type: object
                    properties:
                      prompt_tokens:
                        type: integer
                        example: 12
                      completion_tokens:
                        type: integer
                        example: 1
                      total_tokens:
                        type: integer
                        example: 13
                      cost:
                        type: number
                        description: OpenRouter-reported USD cost; absent for native providers.
                      is_byok:
                        type: boolean
                        description: True if the caller's own provider key was used.
              examples:
                unary:
                  summary: Unary completion
                  value:
                    id: "chatcmpl-9a3f8c2b14d6e7"
                    object: "chat.completion"
                    created: 1747315920
                    model: "claude-sonnet-4-5"
                    choices:
                      - index: 0
                        finish_reason: "stop"
                        message:
                          role: "assistant"
                          content: "Ottawa."
                    usage:
                      prompt_tokens: 12
                      completion_tokens: 1
                      total_tokens: 13
            text/event-stream:
              schema:
                type: string
                description: |
                  Stream of `chat.completion.chunk` events. Each event is `data: <json>\n\n`; the stream ends with `data: [DONE]\n\n`.
              example: |
                data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1747315920,"model":"claude-sonnet-4-5","choices":[{"index":0,"delta":{"role":"assistant","content":"Ott"},"finish_reason":null}]}

                data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1747315920,"model":"claude-sonnet-4-5","choices":[{"index":0,"delta":{"content":"awa."},"finish_reason":null}]}

                data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1747315920,"model":"claude-sonnet-4-5","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14}}

                data: [DONE]
        '400':
          description: Invalid request body, unsupported modality combination, or unregistered model slug. Messages that can't be translated for the provider (for example a tool message whose `tool_call_id` matches no earlier tool call, or an unsupported image) return `invalid_request` with `param` set to the offending field.
        '401':
          description: Unauthorized — missing or invalid API key.
        '402':
          description: Insufficient credits (`credit_limit_exceeded`).
        '404':
          description: User profile not found (`user_not_found`).
        '429':
          description: The provider rate limited the request (`provider_error`).
        '501':
          description: Requested capability not supported by the resolved provider (`provider_unsupported`).
        '502':
          description: Upstream provider error.
      security:
        - bearerAuth: []
  /models:
    get:
      summary: List models
      description: |
        List chat models, preset model chains, and decision models in display groups for the caller.
        Models with `supports_decisions: true` can use `POST /decisions`. `is_classifier_model: true`
        means decisions-only, with no chat path. GPT-6 Luna (`gpt-6-luna`) supports both decisions and chat.
        Model restrictions still apply; appearing in the catalog does not grant permission to use a model.
      operationId: listModels
      tags:
        - Models
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/models?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.models.list()
            for group in response.model_groups:
                print(group)
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope model availability to a specific team. When omitted, uses the authenticated user's default organization.
      responses:
        '200':
          description: Model groups available to the caller.
          content:
            application/json:
              schema:
                type: object
                properties:
                  model_groups:
                    type: array
                    description: Groups of available models. The shape of each group is open and may evolve; treat it as a passthrough payload for the model picker.
                    items:
                      type: object
              examples:
                groups:
                  summary: Model groups
                  value:
                    model_groups:
                      - {}
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have agent access on the requested team.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /models/route:
    post:
      summary: Route a message to a model
      description: |
        Ask Gumloop Chew — the model router behind **Auto** — which model it would use for a given
        message, and why. Chew is a router, not a model: `router` is always `gumloop-chew`, and the
        concrete model it selected is `route.model`.

        This endpoint returns a decision only. It does not run the selected model or its fallbacks.
        The routing judgement consumes credits and is bounded by the caller's model access.

        Omit `models` to use the deduplicated union of Chew's lane chains, not every model the
        caller may use. Restricted candidates can appear with `status: "restricted"`; they are never
        selected or included in `fallback_models`.

        Team scope requires actual team membership, even within the same organization. Personal API
        keys and OAuth are supported; this is not team-key-only.
      operationId: routeModel
      tags:
        - Models
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/models/route' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "input": "Summarize this email thread",
                "models": ["claude-opus-5", "gpt-5.6-luna"]
              }'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                input:
                  description: "The message to route. `message` is accepted as an alias. A string or an array of `{type: text, text: ...}` parts."
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: object
                models:
                  type: array
                  maxItems: 50
                  minItems: 1
                  uniqueItems: true
                  description: Candidate model IDs to choose between. IDs must be nonempty, unique after remapping, and registered. Omit to use Chew's lane-chain union.
                  items:
                    type: string
                history:
                  type: array
                  maxItems: 20
                  description: Prior turns, oldest first, for context.
                  items:
                    type: object
                    additionalProperties: false
                    required: [role, content]
                    properties:
                      role:
                        type: string
                        enum: [user, assistant]
                      content:
                        description: Message text, as a string or text parts. `input` and `message` are accepted as aliases.
                        oneOf:
                          - type: string
                          - type: array
                            items:
                              type: object
                      model:
                        type: string
                        maxLength: 200
                        description: The model that produced this turn. Assistant messages only.
                agent:
                  type: object
                  additionalProperties: false
                  description: Optional context about the agent the message is for. Sharper context produces a sharper route.
                  properties:
                    name:
                      type: string
                      maxLength: 200
                    description:
                      type: string
                      maxLength: 2000
                    system_prompt:
                      type: string
                      maxLength: 20000
                team_id:
                  type: string
                  description: Scope model availability and credit attribution to a team the caller belongs to.
              required:
                - input
      responses:
        '200':
          description: The routing decision.
          content:
            application/json:
              schema:
                type: object
                properties:
                  router:
                    type: string
                    description: Always `gumloop-chew`.
                  route:
                    type: object
                    properties:
                      model:
                        type: string
                        description: The model Chew selected.
                      lane:
                        type: string
                        description: The capability lane of the selected model (`micro`, `light`, `standard`, `plus`, or `max`).
                      verdict_lane:
                        type: string
                        description: The lane the message was judged to need, before any adjustment.
                      adjustment:
                        type: string
                        nullable: true
                        description: "`escalated`, `downgraded`, or null."
                      reasoning_effort:
                        type: string
                        nullable: true
                      fallback_models:
                        type: array
                        description: Eligible models to try, in order, if the selected one is unavailable. Restricted candidates are excluded.
                        items:
                          type: string
                      fail_closed:
                        type: boolean
                        description: True when routing could not complete and a safe default was used.
                  candidates:
                    type: array
                    description: Every candidate considered. Restricted candidates are reported, not selected.
                    items:
                      type: object
                      properties:
                        requested_model:
                          type: string
                        model:
                          type: string
                        lane:
                          type: string
                        basis:
                          type: string
                          description: How the lane was assigned (`catalog`, `price`, or `rating`).
                        status:
                          type: string
                          description: "`eligible` or `restricted`."
              examples:
                route:
                  summary: Illustrative routing decision
                  value:
                    router: gumloop-chew
                    route:
                      model: gpt-5.6-luna
                      lane: light
                      verdict_lane: light
                      adjustment: null
                      fallback_models:
                        - claude-opus-5
                      fail_closed: false
                    candidates:
                      - requested_model: claude-opus-5
                        model: claude-opus-5
                        lane: plus
                        basis: catalog
                        status: eligible
                      - requested_model: gpt-5.6-luna
                        model: gpt-5.6-luna
                        lane: light
                        basis: catalog
                        status: eligible
        '400':
          description: Invalid request body, unknown model IDs, duplicate IDs after remapping, or extra properties.
        '401':
          description: Unauthorized — missing or invalid API key.
        '402':
          description: Credit limit exceeded.
        '403':
          description: Forbidden — Chew is restricted, no supplied candidate is permitted, or the caller is not a member of the requested team.
        '404':
          description: User profile not found.
      security:
        - bearerAuth: []
  /decisions:
    post:
      summary: Create decision
      description: |
        Classify or score information with a model marked `supports_decisions: true`. Jev uses
        OpenRouter; GPT-6 Luna uses OpenAI directly. Both accept the same question schema.
        The endpoint does not select a fallback model. GPT-6 Luna Decisions uses input-only
        pricing with a separate long-context tier, not its chat rates.
      operationId: createDecision
      tags:
        - Models
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/decisions' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "model": "typesafe/jev-1.13",
                "state": {"ticket": "My checkout page shows a blank screen after I click Pay."},
                "questions": {
                  "is_bug": {"type": "noul", "instructions": "Is the customer reporting a software defect?"},
                  "team": {"type": "choice", "instructions": "Which team should own this ticket?",
                           "criteria": {"payments": "Checkout, billing", "frontend": "Rendering, layout", "account": "Login, permissions"}},
                  "urgency": {"type": "score", "instructions": "How urgent is this ticket?",
                              "criteria": ["Can wait for the next release", "Should be fixed this week", "Blocking revenue right now"]}
                }
              }'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.post(
                "https://api.gumloop.com/api/v1/decisions",
                headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
                json={
                    "model": "typesafe/jev-1.13",
                    "state": {"ticket": "My checkout page shows a blank screen after I click Pay."},
                    "questions": {
                        "is_bug": {"type": "noul", "instructions": "Is the customer reporting a software defect?"},
                        "team": {
                            "type": "choice",
                            "instructions": "Which team should own this ticket?",
                            "criteria": {"payments": "Checkout, billing", "frontend": "Rendering, layout", "account": "Login, permissions"},
                        },
                        "urgency": {
                            "type": "score",
                            "instructions": "How urgent is this ticket?",
                            "criteria": ["Can wait for the next release", "Should be fixed this week", "Blocking revenue right now"],
                        },
                    },
                },
            )
            answers = response.json()["answers"]
            if answers["is_bug"]["noul"] > 0.8:
                route_to(answers["team"]["choice"])
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the request to a team. When omitted, uses the authenticated user's default organization.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - model
                - state
                - questions
              properties:
                model:
                  type: string
                  description: Decision-capable model id. Use an `id` from `GET /models` whose `supports_decisions` is `true`, such as `typesafe/jev-1.13` or `gpt-6-luna`.
                  example: "typesafe/jev-1.13"
                state:
                  description: The content to evaluate. A plain string, or a JSON object or array of related context.
                  oneOf:
                    - type: string
                    - type: object
                    - type: array
                      items: {}
                questions:
                  type: object
                  description: Questions keyed by ids you choose. Answers come back under the same ids.
                  additionalProperties:
                    oneOf:
                      - type: object
                        title: Yes/no question
                        required: [type, instructions]
                        properties:
                          type:
                            type: string
                            enum: ["noul"]
                          instructions:
                            description: The question. A string, or a JSON object or array.
                          criteria:
                            type: object
                            description: Optional descriptions of what yes and no mean. Either side may be omitted.
                            properties:
                              "true": {}
                              "false": {}
                      - type: object
                        title: Choice question
                        required: [type, instructions, criteria]
                        properties:
                          type:
                            type: string
                            enum: ["choice"]
                          instructions: {}
                          criteria:
                            type: object
                            description: Option id to description. A description may be `null`.
                            additionalProperties: true
                      - type: object
                        title: Score question
                        required: [type, instructions, criteria]
                        properties:
                          type:
                            type: string
                            enum: ["score"]
                          instructions: {}
                          criteria:
                            type: array
                            description: Ordered rubric, lowest level first.
                            items: {}
                provider:
                  type: object
                  description: OpenRouter provider routing preferences, not forwarded to OpenAI. The server applies its zero-data-retention policy last for OpenRouter. You cannot turn it off.
                session_id:
                  type: string
                  maxLength: 256
                  description: Groups related requests in observability tooling. Not sent to the model.
                trace:
                  type: object
                  nullable: true
                  description: Observability metadata. Unrecognized keys are ignored, not forwarded.
                  properties:
                    trace_id:
                      type: string
                      nullable: true
                    trace_name:
                      type: string
                      nullable: true
                    span_name:
                      type: string
                      nullable: true
                    generation_name:
                      type: string
                      nullable: true
                    parent_span_id:
                      type: string
                      nullable: true
                user:
                  type: string
                  description: A stable id for your end user. The provider uses it for abuse monitoring. OpenAI receives it as `safety_identifier`, truncated to 128 characters.
            example:
              model: "typesafe/jev-1.13"
              state:
                ticket: "My checkout page shows a blank screen after I click Pay."
              questions:
                is_bug:
                  type: "noul"
                  instructions: "Is the customer reporting a software defect?"
                team:
                  type: "choice"
                  instructions: "Which team should own this ticket?"
                  criteria:
                    payments: "Checkout, billing"
                    frontend: "Rendering, layout"
                    account: "Login, permissions"
                urgency:
                  type: "score"
                  instructions: "How urgent is this ticket?"
                  criteria:
                    - "Can wait for the next release"
                    - "Should be fixed this week"
                    - "Blocking revenue right now"
      responses:
        '200':
          description: One answer per question, under the question's id.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: "gen-dec-1790835808-rFEEP39lhplllf50bn9Q"
                  model:
                    type: string
                    description: The exact model version that answered.
                    example: "typesafe/jev-1.13-20260917"
                  provider:
                    type: string
                    example: "TypeSafe"
                  answers:
                    type: object
                    description: Keyed by your question ids. The fields present depend on the question `type`.
                    additionalProperties:
                      type: object
                      properties:
                        type:
                          type: string
                          enum: ["noul", "choice", "score"]
                        noul:
                          type: number
                          description: Probability of yes (`noul` questions).
                        choice:
                          type: string
                          description: The selected option id (`choice` questions).
                        score:
                          type: number
                          description: Weighted position on the rubric (`score` questions).
                        probabilities:
                          type: object
                          additionalProperties:
                            type: number
                        legend:
                          type: object
                          description: Level index to rubric text (`score` questions).
                        confidence:
                          type: number
                          description: How concentrated the distribution is. It does not say whether the answer is right.
                  usage:
                    type: object
                    properties:
                      input_tokens:
                        type: integer
                        example: 380
                      output_tokens:
                        type: integer
                        example: 70
                      cost:
                        type: number
                        description: Provider-reported USD cost. May be 0 or absent.
              example:
                id: "gen-dec-1790835808-rFEEP39lhplllf50bn9Q"
                model: "typesafe/jev-1.13-20260917"
                provider: "TypeSafe"
                answers:
                  is_bug:
                    type: "noul"
                    noul: 0.94
                  team:
                    type: "choice"
                    choice: "payments"
                    confidence: 0.37
                    probabilities:
                      payments: 0.58
                      frontend: 0.42
                      account: 0.0
                  urgency:
                    type: "score"
                    score: 1.92
                    confidence: 0.88
                    legend:
                      "0": "Can wait for the next release"
                      "1": "Should be fixed this week"
                      "2": "Blocking revenue right now"
                    probabilities:
                      "0": 0.0
                      "1": 0.07
                      "2": 0.93
                usage:
                  input_tokens: 380
                  output_tokens: 70
                  cost: 0.0
        '400':
          description: >-
            Invalid request body (`invalid_request`, with `details.validation_errors`). A model that is
            unregistered, not permitted for your organization, or not a decision model (`model_unavailable`,
            with `details.reason` one of `not_registered`, `not_permitted`, `not_decision_model`, `proxy_unsupported`).
            A request the provider rejected (`provider_error`, with the provider's own text in `details.provider_message`).
        '401':
          description: Unauthorized. The API key is missing or invalid.
        '402':
          description: >-
            Insufficient credits (`credit_limit_exceeded`), or the provider account is out of credits
            (`provider_error`).
        '403':
          description: Your role does not include agent access (`ACCESS_AGENTS`).
        '404':
          description: User profile not found (`user_profile_not_found`).
        '422':
          description: >-
            OpenAI refused one or more questions (`provider_error`). The refused question ids appear
            in `details.provider_message`. No partial answers are returned.
        '429':
          description: The provider rate limited the request (`provider_error`). Wait for `Retry-After` seconds before you retry.
        '502':
          description: Upstream provider error.
      security:
        - bearerAuth: []
  /agents/{agent_id}/versions:
    get:
      summary: List agent versions
      description: |
        List the immutable versions of an agent, newest first. Each entry is a point-in-time snapshot of the agent's configuration.

        Requires configuration access on the agent — callers limited to using the agent (no configuration access) get a `403`.
      operationId: listAgentVersions
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/versions?page_size=20' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.list_versions("abc123DEFghiJKL")
            for version in response.versions:
                print(version.id, version.major_version, version.is_deployed)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent whose versions to list. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of versions to return per page. Clamped to 1–100.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque pagination cursor returned by a prior call as `next_cursor`.
      responses:
        '200':
          description: Versions of the agent, newest first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  versions:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Unique version identifier. Pass this as `version_id` to retrieve the full version.
                          example: "gv_a1b2c3d4"
                        agent_id:
                          type: string
                          example: "abc123DEFghiJKL"
                        major_version:
                          type: integer
                          description: Incrementing version number for the agent.
                          example: 3
                        name:
                          type: string
                          description: The agent's name as of this version.
                          example: "Sales research agent"
                        is_deployed:
                          type: boolean
                          nullable: true
                          description: '`true` for the version currently deployed; `null` otherwise.'
                          example: true
                        created_at:
                          type: string
                          format: date-time
                          nullable: true
                          example: "2026-05-16T09:10:00Z"
                        creator:
                          type: object
                          nullable: true
                          description: The user who created this version. `null` when unknown.
                          properties:
                            id:
                              type: string
                              nullable: true
                              example: "user_8c2a1b"
                            first_name:
                              type: string
                              nullable: true
                              example: "Ada"
                            last_name:
                              type: string
                              nullable: true
                              example: "Lovelace"
                            email:
                              type: string
                              nullable: true
                              example: "ada@example.com"
                            profile_picture:
                              type: string
                              nullable: true
                              example: "https://example.com/avatars/ada.png"
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor to pass as `cursor` on the next request. `null` when there are no more results.
              examples:
                multiple:
                  summary: Multiple versions
                  value:
                    versions:
                      - id: "gv_a1b2c3d4"
                        agent_id: "abc123DEFghiJKL"
                        major_version: 3
                        name: "Sales research agent"
                        is_deployed: true
                        created_at: "2026-05-16T09:10:00Z"
                        creator:
                          id: "user_8c2a1b"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                      - id: "gv_e5f6g7h8"
                        agent_id: "abc123DEFghiJKL"
                        major_version: 2
                        name: "Sales research agent"
                        is_deployed: null
                        created_at: "2026-05-02T09:11:00Z"
                        creator:
                          id: "user_8c2a1b"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                    next_cursor: "eyJjcmVhdGVkX3RzIjoiMjAyNi0wNS0wMlQwOToxMTowMFoifQ=="
                empty:
                  summary: No versions
                  value:
                    versions: []
                    next_cursor: null
        '400':
          description: Bad request — `page_size` is not an integer.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have configuration access on the agent.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /agents/{agent_id}/versions/{version_id}:
    get:
      summary: Retrieve agent version
      description: |
        Retrieve one immutable agent version: its full configuration (`composition`) plus the structured `changes` relative to the version before it. Use it to export an agent's configuration or to audit what changed between versions.

        `changes` is `null` for the first version of an agent, since there is no predecessor to diff against. Versions created before attachment snapshots were recorded report `composition.complete: false` (and `changes.attachment_changes_complete: false`); their `skill_ids` and `knowledge_sources` are `null` rather than empty. Skill file contents are never included, and this endpoint is read-only — it cannot restore or deploy a version.

        Requires configuration access on the agent — callers limited to using the agent (no configuration access) get a `403`.
      operationId: retrieveAgentVersion
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/versions/gv_a1b2c3d4' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.get_version("abc123DEFghiJKL", "gv_a1b2c3d4")
            print(response.version.composition.system_prompt)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent the version belongs to. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: path
          name: version_id
          required: true
          schema:
            type: string
          description: ID of the version to retrieve, from `GET /agents/{agent_id}/versions`.
      responses:
        '200':
          description: The requested agent version.
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "gv_a1b2c3d4"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      major_version:
                        type: integer
                        example: 3
                      name:
                        type: string
                        example: "Sales research agent"
                      is_deployed:
                        type: boolean
                        nullable: true
                        description: '`true` for the version currently deployed; `null` otherwise.'
                        example: true
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-16T09:10:00Z"
                      creator:
                        type: object
                        nullable: true
                        description: The user who created this version. `null` when unknown.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      composition:
                        type: object
                        description: The agent's full configuration as of this version.
                        properties:
                          complete:
                            type: boolean
                            description: '`false` for legacy versions saved without an attachment snapshot; `skill_ids` and `knowledge_sources` are `null` in that case.'
                            example: true
                          schema_version:
                            type: integer
                            nullable: true
                            description: Schema version of the stored snapshot. `null` when the snapshot is missing.
                            example: 1
                          name:
                            type: string
                            example: "Sales research agent"
                          description:
                            type: string
                            nullable: true
                            example: "Researches accounts and drafts outreach"
                          model_name:
                            type: string
                            example: "anthropic/claude-sonnet-4"
                          system_prompt:
                            type: string
                            nullable: true
                            example: "You are a B2B sales research assistant."
                          tools:
                            type: array
                            description: Tool configurations attached to the agent. Credentials and secrets are stripped.
                            items:
                              type: object
                          resources:
                            type: array
                            items:
                              type: object
                          metadata:
                            type: object
                          skill_ids:
                            type: array
                            nullable: true
                            description: IDs of skills attached at this version. `null` when the snapshot is incomplete.
                            items:
                              type: string
                          knowledge_sources:
                            type: array
                            nullable: true
                            description: Brain knowledge sources attached at this version. `null` when the snapshot is incomplete.
                            items:
                              type: object
                              properties:
                                connector_id:
                                  type: string
                                config:
                                  type: object
                                  nullable: true
                  changes:
                    type: object
                    nullable: true
                    description: Structured diff against the preceding version. `null` for the agent's first version.
                    properties:
                      base_version_id:
                        type: string
                        description: ID of the version this diff is computed against.
                        example: "gv_e5f6g7h8"
                      attachment_changes_complete:
                        type: boolean
                        description: '`false` when either version lacks an attachment snapshot, so skill and knowledge-source changes may be incomplete.'
                        example: true
                      field_changes:
                        type: array
                        description: Changed agent fields. Long text fields (such as `system_prompt`) report `text_hunks` instead of whole values.
                        items:
                          type: object
                      tool_changes:
                        type: array
                        description: Added, removed, reordered, or edited tools. Secrets are stripped and custom MCP URLs are redacted.
                        items:
                          type: object
                      skill_changes:
                        type: array
                        items:
                          type: object
                          properties:
                            skill_id:
                              type: string
                            status:
                              type: string
                      knowledge_source_changes:
                        type: array
                        items:
                          type: object
              examples:
                withChanges:
                  summary: Version with changes
                  value:
                    version:
                      id: "gv_a1b2c3d4"
                      agent_id: "abc123DEFghiJKL"
                      major_version: 3
                      name: "Sales research agent"
                      is_deployed: true
                      created_at: "2026-05-16T09:10:00Z"
                      creator:
                        id: "user_8c2a1b"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                      composition:
                        complete: true
                        schema_version: 1
                        name: "Sales research agent"
                        description: "Researches accounts and drafts outreach"
                        model_name: "anthropic/claude-sonnet-4"
                        system_prompt: "You are a B2B sales research assistant."
                        tools: []
                        resources: []
                        metadata: {}
                        skill_ids: ["skill_2b9c"]
                        knowledge_sources: []
                    changes:
                      base_version_id: "gv_e5f6g7h8"
                      attachment_changes_complete: true
                      field_changes:
                        - field: "system_prompt"
                          status: "modified"
                          text_hunks:
                            - old_start: 0
                              old_end: 1
                              new_start: 0
                              new_end: 1
                              old_text: "You are a sales assistant."
                              new_text: "You are a B2B sales research assistant."
                      tool_changes: []
                      skill_changes:
                        - skill_id: "skill_2b9c"
                          status: "added"
                      knowledge_source_changes: []
                firstVersion:
                  summary: First version (no diff)
                  value:
                    version:
                      id: "gv_z9y8x7w6"
                      agent_id: "abc123DEFghiJKL"
                      major_version: 1
                      name: "Sales research agent"
                      is_deployed: null
                      created_at: "2026-04-02T09:11:00Z"
                      creator:
                        id: "user_8c2a1b"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                      composition:
                        complete: true
                        schema_version: 1
                        name: "Sales research agent"
                        description: null
                        model_name: "anthropic/claude-sonnet-4"
                        system_prompt: "You are a sales assistant."
                        tools: []
                        resources: []
                        metadata: {}
                        skill_ids: []
                        knowledge_sources: []
                    changes: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have configuration access on the agent.
        '404':
          description: Agent or version not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /agents/{agent_id}/skills:
    patch:
      summary: Attach or detach agent skills
      description: |
        Attach and/or detach skills on an agent using deltas. This is **not** a replace-list: skills you don't mention are left untouched.

        - The operation is idempotent. Re-attaching a skill that's already attached (or detaching one that isn't) is reported under `already_attached` / `already_detached` rather than failing.
        - A skill ID may not appear in both `attach` and `detach`.
        - Up to 100 unique skill IDs total (`attach` + `detach`) per request.
        - Attaching requires `INVOKE` permission on the skill. Detaching is permissive so stale attachments can always be removed.
      operationId: updateAgentSkills
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/skills' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "attach": ["skill_2b9c"],
                "detach": ["skill_7f1a"]
              }'
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent whose skills to update. Also accepts the reserved aliases `gumball` and `analytics`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                attach:
                  type: array
                  description: Skill IDs to attach. Ignored if already attached.
                  items:
                    type: string
                  default: []
                detach:
                  type: array
                  description: Skill IDs to detach. Ignored if not attached.
                  items:
                    type: string
                  default: []
            examples:
              attachAndDetach:
                summary: Attach one skill and detach another
                value:
                  attach: ["skill_2b9c"]
                  detach: ["skill_7f1a"]
      responses:
        '200':
          description: The resulting skill set plus what this request changed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  skill_ids:
                    type: array
                    description: Full set of skills attached to the agent after this request.
                    items:
                      type: string
                  attached:
                    type: array
                    description: Skills newly attached by this request.
                    items:
                      type: string
                  detached:
                    type: array
                    description: Skills newly detached by this request.
                    items:
                      type: string
                  already_attached:
                    type: array
                    description: Requested attaches that were already attached (no-op).
                    items:
                      type: string
                  already_detached:
                    type: array
                    description: Requested detaches that weren't attached (no-op).
                    items:
                      type: string
              examples:
                updated:
                  summary: One attached, one detached
                  value:
                    agent_id: "abc123DEFghiJKL"
                    skill_ids: ["skill_2b9c"]
                    attached: ["skill_2b9c"]
                    detached: ["skill_7f1a"]
                    already_attached: []
                    already_detached: []
        '400':
          description: Invalid request — e.g. an ID appears in both `attach` and `detach`, more than 100 unique IDs, or a skill to attach doesn't exist or isn't active (offending IDs returned in `error.details.skill_ids`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller lacks update access to the agent, or lacks `INVOKE` permission on a skill being attached (offending IDs in `error.details.skill_ids`).
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /agents/{agent_id}/mcp-servers:
    get:
      summary: List agent MCP servers
      description: |
        List the MCP servers (connectors) attached to an agent. Sensitive fields such as `secret_id` and `mcp_server_url` are scrubbed from the response.
      operationId: listAgentMcpServers
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/mcp-servers' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
      responses:
        '200':
          description: The MCP servers attached to the agent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  mcp_servers:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentTool'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access to this agent's configuration.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /agents/{agent_id}/mcp-servers/{server_id}:
    put:
      summary: Attach or update an agent MCP server
      description: |
        Attach a connector to an agent, or update its settings if it is already attached (upsert).

        `server_id` comes from your MCP catalog (`GET /mcp/servers`); custom MCP servers you added in Settings are listed there too, keyed by their secret. Identity is taken from the path, so the body carries settings only: which account to use, approval mode, per-tool approvals and tool restrictions.

        Attach may succeed before OAuth is completed; `auth_status` reports the catalog's authentication state.
      operationId: attachAgentMcpServer
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PUT 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/mcp-servers/srv_github' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{}'
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: path
          name: server_id
          required: true
          schema:
            type: string
          description: ID of the MCP server from the caller's catalog.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectorConfig'
            examples:
              empty:
                summary: Attach with default config
                value: {}
              configured:
                summary: Ask before writes, block one tool
                value:
                  approval_mode: write
                  restricted_tools: ["send_email"]
      responses:
        '200':
          description: The attached (or updated) MCP server.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  mcp_server:
                    allOf:
                      - $ref: '#/components/schemas/AgentTool'
                    description: The resulting connector entry, with secrets and server URL redacted.
                  created:
                    type: boolean
                    description: '`true` if the server was newly attached; `false` if an existing attachment was updated.'
                    example: true
                  auth_status:
                    type: string
                    nullable: true
                    description: Catalog authentication status for the server (e.g. whether OAuth has been completed).
                    example: "authenticated"
                  version:
                    type: integer
                    nullable: true
                    description: The agent's configuration version after this change.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller lacks update access to the agent, or the MCP server is restricted for the organization (`mcp_server_restricted`).
        '404':
          description: Agent not found, or the server ID is not in the caller's catalog (`mcp_server_not_found`).
        '409':
          description: The agent has duplicate entries for this server (`ambiguous_mcp_server`); remove them and retry.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    delete:
      summary: Detach an agent MCP server
      description: |
        Detach an MCP server (connector) from an agent. This is idempotent — detaching a server that isn't attached returns `detached: false` rather than an error.
      operationId: detachAgentMcpServer
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/mcp-servers/srv_github' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: path
          name: server_id
          required: true
          schema:
            type: string
          description: ID of the MCP server to detach.
      responses:
        '200':
          description: The detach result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  server_id:
                    type: string
                    example: "srv_github"
                  detached:
                    type: boolean
                    description: '`true` if the server was attached and is now removed; `false` if it was not attached.'
                    example: true
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access to this agent.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /agents/{agent_id}/abilities:
    patch:
      summary: Update agent abilities
      description: |
        Turn the agent's native abilities on or off and set their options: Web Search, Web Fetch, Image Generation, Search Past Conversations, Ask Question, Tool Discovery, Manage Evaluations and Browser.

        Send only the abilities you want to change. Enabling an ability that is already on keeps its saved settings unless you send new ones. Abilities your organization's policy denies (for example Browser) answer `403`.
      operationId: updateAgentAbilities
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/abilities' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "web_search": {"enabled": true, "provider": "exa"},
                "ask_question": {"enabled": false}
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.update_abilities(
                "abc123DEFghiJKL",
                web_search={"enabled": True, "provider": "exa"},
                ask_question={"enabled": False},
            )
            print(response.abilities.web_search.enabled)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/AgentAbilities'
                - type: object
                  properties:
                    version:
                      type: integer
                      description: Optional. The `version` you last read; the update is refused with `409 agent_version_conflict` if the agent changed since.
      responses:
        '200':
          description: The agent's abilities after the change.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                    example: "abc123DEFghiJKL"
                  abilities:
                    $ref: '#/components/schemas/AgentAbilities'
                  version:
                    type: integer
                    nullable: true
                    example: 5
        '400':
          description: Invalid request body, or the agent is a platform agent (`agent_not_customizable`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller lacks update access, or an ability is denied by organization policy.
        '404':
          description: Agent not found.
        '409':
          description: '`agent_version_conflict` — the agent changed since the `version` you sent.'
      security:
        - bearerAuth: []
  /agents/{agent_id}/knowledge-sources:
    get:
      summary: List agent knowledge sources
      description: Brain sources attached to the agent, with the scope each one is read through. The same list is inlined on `GET /agents/{agent_id}` as `knowledge_sources`.
      operationId: listAgentKnowledgeSources
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/knowledge-sources' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.list_knowledge_sources("abc123DEFghiJKL")
            for source in response.knowledge_sources:
                print(source.connector_id, source.config)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent.
      responses:
        '200':
          description: Attached sources.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  knowledge_sources:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentKnowledgeSource'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot view knowledge sources on this agent, or Brain is restricted for their role (`feature_access_restricted`).
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/knowledge-sources/{connector_id}:
    put:
      summary: Attach or scope an agent knowledge source
      description: |
        Attach a Brain source to the agent, or change its scope if it is already attached. Idempotent: sending the same scope twice reports `already_present`.

        `config: null` attaches the whole source. To narrow it, send `exclusions` (everything except these), or `mode: include_only` with `inclusions` (only these). Item IDs come from `GET /brain/sources/{connector_id}/files`.

        The source must be readable by the caller and live in the agent's workspace (personal sources on personal agents, team sources on team agents).
      operationId: attachAgentKnowledgeSource
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PUT 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/knowledge-sources/PFqdAMir8PA2Xc6qcszSN9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"config": {"mode": "include_only", "inclusions": [{"type": "container", "id": "folder_91ab"}]}}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            # Whole source
            client.agents.attach_knowledge_source("abc123DEFghiJKL", "PFqdAMir8PA2Xc6qcszSN9")

            # One folder only
            client.agents.attach_knowledge_source(
                "abc123DEFghiJKL",
                "PFqdAMir8PA2Xc6qcszSN9",
                config={"mode": "include_only", "inclusions": [{"type": "container", "id": "folder_91ab"}]},
            )
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: connector_id
          required: true
          schema:
            type: string
          description: Brain source ID from `GET /brain/sources`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                config:
                  $ref: '#/components/schemas/KnowledgeSourceScope'
            examples:
              whole:
                summary: Whole source
                value:
                  config: null
              folder:
                summary: One folder only
                value:
                  config:
                    mode: include_only
                    inclusions:
                      - type: container
                        id: "folder_91ab"
      responses:
        '200':
          description: The attachment after the change.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  knowledge_source:
                    $ref: '#/components/schemas/AgentKnowledgeSource'
                  outcome:
                    type: string
                    enum: [attached, updated, already_present]
        '400':
          description: Invalid scope (`knowledge_source_invalid_config`), source in another workspace (`knowledge_source_scope_mismatch`), or a platform agent (`agent_not_customizable`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller lacks update access, cannot read the source, or Brain is restricted for their role.
        '404':
          description: Agent or source not found (`knowledge_source_not_found`).
      security:
        - bearerAuth: []
    delete:
      summary: Detach an agent knowledge source
      description: Detach a Brain source from the agent. Idempotent; detaching a source that is not attached answers with `detached` set to `false`.
      operationId: detachAgentKnowledgeSource
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/knowledge-sources/PFqdAMir8PA2Xc6qcszSN9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.agents.detach_knowledge_source("abc123DEFghiJKL", "PFqdAMir8PA2Xc6qcszSN9")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: connector_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The detach result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  connector_id:
                    type: string
                  detached:
                    type: boolean
        '400':
          description: '`agent_not_customizable` — platform agents cannot be configured.'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access to this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/subagents/{subagent_id}:
    put:
      summary: Attach a subagent
      description: |
        Let the agent delegate to another agent. Idempotent: attaching an agent that is already allowed returns `changed: false`.

        The subagent must be in the same workspace as the agent, or shared with that workspace. Whether the agent may also clone itself is `metadata.subagent.allow_self_clone` on `PATCH /agents/{agent_id}`.
      operationId: attachAgentSubagent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PUT 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/subagents/xyz789MNOpqrSTU' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.attach_subagent("abc123DEFghiJKL", "xyz789MNOpqrSTU")
            print(response.subagent_ids)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: subagent_id
          required: true
          schema:
            type: string
          description: ID of the agent to allow as a subagent.
      responses:
        '200':
          description: The allowed subagents after the change.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  subagent_ids:
                    type: array
                    items:
                      type: string
                  changed:
                    type: boolean
        '400':
          description: '`subagent_not_in_workspace` — the subagent is not reachable from the agent''s workspace; or `agent_not_customizable`.'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access to this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
    delete:
      summary: Detach a subagent
      description: Stop the agent delegating to `subagent_id`. Idempotent.
      operationId: detachAgentSubagent
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/subagents/xyz789MNOpqrSTU' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.agents.detach_subagent("abc123DEFghiJKL", "xyz789MNOpqrSTU")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: subagent_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The allowed subagents after the change.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  subagent_ids:
                    type: array
                    items:
                      type: string
                  changed:
                    type: boolean
        '400':
          description: '`agent_not_customizable` — platform agents cannot be configured.'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access to this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/triggers:
    get:
      summary: List agent triggers
      description: |
        Triggers on the agent, newest first, using the shared cursor contract (`page_size`, `cursor`, `next_cursor`). Every trigger type is listed; only `schedule` and `webhook` can be created or edited through the API.

        Webhook URLs are not included. Fetch one with `GET /agents/{agent_id}/triggers/{trigger_id}/webhook-url`.
      operationId: listAgentTriggers
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/triggers' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.agents.list_triggers("abc123DEFghiJKL")
            for trigger in response.triggers:
                print(trigger.type, trigger.name, trigger.enabled)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: page_size
          schema:
            type: integer
            default: 24
            maximum: 100
        - in: query
          name: cursor
          schema:
            type: string
      responses:
        '200':
          description: One page of triggers.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  triggers:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentTrigger'
                  next_cursor:
                    type: string
                    nullable: true
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot view triggers on this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
    post:
      summary: Create an agent trigger
      description: |
        Create a `schedule` or `webhook` trigger.

        A schedule takes exactly one of `cron_expression` (recurring, five-field cron) or `run_at` (one time, ISO 8601). `timezone` defaults to the caller's profile timezone. A webhook takes `prompt` and returns `webhook_url` in this response only; the URL embeds the trigger's secret, so store it or fetch it again from the reveal endpoint.

        Connector-event triggers and agent-built triggers cannot be created here. Team API keys are not accepted: a trigger runs later on the creating member's credentials.
      operationId: createAgentTrigger
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/triggers' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "type": "schedule",
                "name": "Morning digest",
                "prompt": "Summarize overnight support tickets.",
                "cron_expression": "0 9 * * 1-5",
                "timezone": "America/Los_Angeles"
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            schedule = client.agents.create_trigger(
                "abc123DEFghiJKL",
                type="schedule",
                name="Morning digest",
                prompt="Summarize overnight support tickets.",
                cron_expression="0 9 * * 1-5",
                timezone="America/Los_Angeles",
            )

            webhook = client.agents.create_trigger(
                "abc123DEFghiJKL", type="webhook", prompt="Handle the incoming ticket."
            )
            print(webhook.trigger.webhook_url)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, prompt]
              properties:
                type:
                  type: string
                  enum: [schedule, webhook]
                prompt:
                  type: string
                  description: The message the agent receives when the trigger fires.
                name:
                  type: string
                  nullable: true
                  description: Label shown in the triggers list.
                cron_expression:
                  type: string
                  nullable: true
                  description: Schedule only. Five-field cron, e.g. `0 9 * * 1-5`.
                run_at:
                  type: string
                  format: date-time
                  nullable: true
                  description: Schedule only. One-time run; must be in the future. Interpreted in `timezone` when it has no offset.
                timezone:
                  type: string
                  nullable: true
                  description: Schedule only. IANA name. Defaults to the caller's profile timezone.
                pass_raw_data:
                  type: boolean
                  default: false
                  description: Webhook only. Forward the request body to the agent instead of `prompt`.
                enabled:
                  type: boolean
                  default: true
                max_failures:
                  type: integer
                  nullable: true
                  description: Consecutive failures before the trigger disables itself. Defaults to 3 (1 for one-time runs).
            examples:
              schedule:
                summary: Weekday schedule
                value:
                  type: schedule
                  name: "Morning digest"
                  prompt: "Summarize overnight support tickets."
                  cron_expression: "0 9 * * 1-5"
                  timezone: "America/Los_Angeles"
              webhook:
                summary: Webhook
                value:
                  type: webhook
                  prompt: "Handle the incoming ticket."
      responses:
        '201':
          description: The created trigger. For a webhook, `webhook_url` is populated here only.
          content:
            application/json:
              schema:
                type: object
                properties:
                  trigger:
                    $ref: '#/components/schemas/AgentTrigger'
        '400':
          description: Invalid body (`trigger_invalid_cron_expression`, `trigger_run_at_in_past`, `trigger_invalid_timezone`), or a platform agent (`agent_not_customizable`).
        '401':
          description: Unauthorized — missing or invalid API key, or a team API key.
        '403':
          description: Forbidden — the caller cannot create triggers on this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/triggers/{trigger_id}:
    patch:
      summary: Update an agent trigger
      description: |
        Only the fields you send change. Any trigger can be renamed, reworded, paused and resumed (`name`, `prompt`, `enabled`, `max_failures`). Schedule fields apply to `schedule` triggers and `pass_raw_data` to `webhook` triggers; sending them on another type answers `400 trigger_type_not_editable`. A schedule keeps its kind: send `cron_expression` on a recurring trigger and `run_at` on a one-time one.

        Team API keys are not accepted.
      operationId: updateAgentTrigger
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/triggers/trg_7f1a2b' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"cron_expression": "0 9 * * 1", "enabled": false}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.agents.update_trigger("abc123DEFghiJKL", "trg_7f1a2b", cron_expression="0 9 * * 1", enabled=False)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: trigger_id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: string
                name:
                  type: string
                cron_expression:
                  type: string
                run_at:
                  type: string
                  format: date-time
                timezone:
                  type: string
                pass_raw_data:
                  type: boolean
                enabled:
                  type: boolean
                max_failures:
                  type: integer
      responses:
        '200':
          description: The updated trigger.
          content:
            application/json:
              schema:
                type: object
                properties:
                  trigger:
                    $ref: '#/components/schemas/AgentTrigger'
        '400':
          description: '`trigger_type_not_editable`, `trigger_schedule_kind_fixed`, `trigger_schedule_not_applicable`, an invalid cron or timezone, or `agent_not_customizable`.'
        '401':
          description: Unauthorized — missing or invalid API key, or a team API key.
        '403':
          description: Forbidden — the caller cannot edit, enable or disable this trigger.
        '404':
          description: Agent or trigger not found.
      security:
        - bearerAuth: []
    delete:
      summary: Delete an agent trigger
      description: Delete a `schedule` or `webhook` trigger. Other trigger types answer `400 trigger_type_not_editable`. Team API keys are not accepted.
      operationId: deleteAgentTrigger
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/triggers/trg_7f1a2b' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.agents.delete_trigger("abc123DEFghiJKL", "trg_7f1a2b")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of a custom agent.
        - in: path
          name: trigger_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The delete result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  trigger_id:
                    type: string
                  deleted:
                    type: boolean
        '400':
          description: '`trigger_type_not_editable` or `agent_not_customizable`.'
        '401':
          description: Unauthorized — missing or invalid API key, or a team API key.
        '403':
          description: Forbidden — the caller cannot delete this trigger.
        '404':
          description: Agent or trigger not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/triggers/{trigger_id}/webhook-url:
    get:
      summary: Get a webhook trigger's URL
      description: The inbound URL for a `webhook` trigger. It embeds the trigger's secret, so it is returned here and on create only, never in lists or agent reads.
      operationId: getAgentTriggerWebhookUrl
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/triggers/trg_7f1a2b/webhook-url' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            print(client.agents.get_trigger_webhook_url("abc123DEFghiJKL", "trg_7f1a2b").webhook_url)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
        - in: path
          name: trigger_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The webhook URL.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  trigger_id:
                    type: string
                  webhook_url:
                    type: string
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot view this trigger.
        '404':
          description: Agent or trigger not found, or the trigger is not a webhook.
      security:
        - bearerAuth: []
  /agents/{agent_id}/app-rules:
    get:
      summary: List agent app rules
      description: Connector rules the agent has authored for itself (which tools it may call on a connector, and when). Read only; rules are created by the agent, not through the API.
      operationId: listAgentAppRules
      tags:
        - Agents
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/app-rules' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            for rule in client.agents.list_app_rules("abc123DEFghiJKL").app_rules:
                print(rule.server_id, rule.name)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The agent's rules.
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent_id:
                    type: string
                  app_rules:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentAppRule'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot view app rules on this agent.
        '404':
          description: Agent not found.
      security:
        - bearerAuth: []
  /agents/{agent_id}/sessions:
    get:
      summary: List sessions
      description: |
        List sessions for an agent with cursor-based pagination, optional filtering, and search.
      operationId: listSessions
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/sessions?page_size=20' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.sessions.list("abc123DEFghiJKL")
            for session in response.sessions:
                print(session.id, session.state)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent whose sessions to list. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of sessions to return per page. Defaults to `20`, maximum `100`.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Cursor for the next page of results. Use the `next_cursor` value from a previous response.
        - in: query
          name: search
          required: false
          schema:
            type: string
          description: Free-text search query to filter sessions by name or content. Also accepted as `search_query`.
        - in: query
          name: sort_order
          required: false
          schema:
            type: string
          description: Sort order for the results (e.g. `newest` or `oldest`).
        - in: query
          name: type
          required: false
          schema:
            type: string
          description: Filter sessions by type (e.g. `api`, `web`, `slack`).
        - in: query
          name: state
          required: false
          schema:
            type: string
            enum: [processing, completed, failed, queued, idle]
          description: Filter sessions by state.
        - in: query
          name: creator_user_id
          required: false
          schema:
            type: string
          description: Filter sessions by the user who created them.
        - in: query
          name: trigger_id
          required: false
          schema:
            type: string
          description: Filter sessions by the trigger that initiated them.
      responses:
        '200':
          description: A paginated list of sessions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  sessions:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          example: "sess_xYz789AbCd"
                        agent_id:
                          type: string
                          example: "abc123DEFghiJKL"
                        name:
                          type: string
                          nullable: true
                          example: "Research task"
                        type:
                          type: string
                          nullable: true
                          example: "api"
                        messages:
                          type: array
                          items:
                            type: object
                          description: Messages are not included in list responses. Use the retrieve session endpoint to get the full message history.
                        created_at:
                          type: string
                          format: date-time
                          nullable: true
                          example: "2026-05-15T14:32:00Z"
                        state:
                          type: string
                          nullable: true
                          enum: [processing, completed, failed, queued, idle, approval_required]
                          example: "completed"
                        agent_name:
                          type: string
                          nullable: true
                          example: "Sales research agent"
                        creator:
                          type: object
                          nullable: true
                          properties:
                            id:
                              type: string
                              nullable: true
                            first_name:
                              type: string
                              nullable: true
                            last_name:
                              type: string
                              nullable: true
                            email:
                              type: string
                              nullable: true
                            profile_picture:
                              type: string
                              nullable: true
                        usage:
                          type: object
                          description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                          properties:
                            credit_cost:
                              type: number
                              nullable: true
                              description: Total credits consumed by the session, including tool and flow (workflow) credits.
                              example: 12.5
                            tool_credit_cost:
                              type: number
                              nullable: true
                              description: Credits consumed by tool calls.
                              example: 4
                            flow_credit_cost:
                              type: number
                              nullable: true
                              description: Credits consumed by workflow (flow) runs invoked by the agent.
                              example: 1.5
                            input_tokens:
                              type: integer
                              nullable: true
                              description: Total input tokens across the session.
                              example: 18432
                            cached_input_tokens:
                              type: integer
                              nullable: true
                              description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                              example: 12288
                            cache_write_input_tokens:
                              type: integer
                              nullable: true
                              description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                              example: 2048
                            output_tokens:
                              type: integer
                              nullable: true
                              description: Total output tokens across the session.
                              example: 2043
                            reasoning_tokens:
                              type: integer
                              nullable: true
                              description: Total reasoning (thinking) tokens across the session.
                              example: 512
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor to pass as the `cursor` query parameter to retrieve the next page. `null` when there are no more results.
                    example: "eyJzb3J0X3ZhbHVlIjoiMjAyNi0wNS0xNVQxNDozMjowMFoiLCJpbnRlcmFjdGlvbl9pZCI6InNlc3NfeFl6Nzg5QWJDZCIsInNvcnRfYnkiOiJuZXdlc3QifQ=="
              examples:
                paginated:
                  summary: First page of sessions
                  value:
                    sessions:
                      - id: "sess_xYz789AbCd"
                        agent_id: "abc123DEFghiJKL"
                        name: "Research task"
                        type: "api"
                        messages: []
                        created_at: "2026-05-15T14:32:00Z"
                        state: "completed"
                        agent_name: "Sales research agent"
                        creator:
                          id: "user_2b9d71f0"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                        usage:
                          credit_cost: 12.5
                          tool_credit_cost: 4
                          flow_credit_cost: 1.5
                          input_tokens: 18432
                          cached_input_tokens: 12288
                          cache_write_input_tokens: 2048
                          output_tokens: 2043
                          reasoning_tokens: 512
                      - id: "sess_AbC123dEfG"
                        agent_id: "abc123DEFghiJKL"
                        name: "Follow-up call"
                        type: "web"
                        messages: []
                        created_at: "2026-05-14T09:15:00Z"
                        state: "completed"
                        agent_name: "Sales research agent"
                        creator:
                          id: "user_2b9d71f0"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                    next_cursor: "eyJzb3J0X3ZhbHVlIjoiMjAyNi0wNS0xNFQwOToxNTowMFoifQ=="
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the agent.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    post:
      summary: Create session
      description: |
        Create a new session for an agent. When `input` is provided, the message is enqueued and the agent begins processing — the response returns `202` with the session in `processing` or `queued` state. When `input` is omitted, an idle session stub is created and the response returns `201`.

        `agent_id` also accepts the reserved aliases `gumball` and `analytics`, which resolve to your personal Gumball and analytics agents (created on first use).

        ### Streaming the response

        `api.gumloop.com` only serves the non-streaming response above. To stream agent output as it's produced, send the same request body (with `stream: true`) to the streaming host instead:

        `POST https://ws.gumloop.com/api/v1/agents/{agent_id}/sessions`

        The response is `text/event-stream` (Server-Sent Events). With the Python SDK, `client.sessions.stream(agent_id, input="...")` routes to `ws.gumloop.com` automatically and yields parsed `StreamEvent` objects.

        If you send `stream: true` to `api.gumloop.com` by mistake, the response is a `400` whose body contains the correct streaming host so you can retry against it.
      operationId: createSession
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/agents/abc123DEFghiJKL/sessions' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"input": "Research Acme Corp and draft a brief."}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.sessions.create(
                "abc123DEFghiJKL",
                input="Research Acme Corp and draft a brief.",
            )
            print(response.session.id, response.session.state)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent to start a session on. Also accepts the reserved aliases `gumball` and `analytics`.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                input:
                  type: string
                  description: The first user message for the session. Also accepted as `message` for backwards compatibility. When omitted, an idle session is created with no messages.
                  example: "Research Acme Corp and draft a brief."
                session_id:
                  type: string
                  description: Caller-supplied session ID. When omitted, the server generates one. If provided and the ID already exists, the request returns `409 session_already_exists`.
                  example: "sess_xYz789AbCd"
                name:
                  type: string
                  maxLength: 256
                  description: Optional display name for the session. Leading and trailing whitespace is removed before the 1-256 character limit is applied, so an empty or whitespace-only value is rejected. A name you supply is kept; the automatic title only fills in sessions created without one. Rename the session later with `PATCH /sessions/{session_id}`.
                  example: "Turn-on request #4821"
                metadata:
                  type: object
                  description: Arbitrary key/value metadata attached to the session. Stored under `metadata.client`.
                stream:
                  type: boolean
                  default: false
                  description: Must be `false` (or omitted) when calling `api.gumloop.com`. Set to `true` only when calling `ws.gumloop.com` (see the streaming section above).
      responses:
        '201':
          description: Idle session created. Returned when the request body has no `input` — a session stub is created and no agent run is started.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      messages:
                        type: array
                        items:
                          type: object
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-15T14:32:00Z"
                      state:
                        type: string
                        nullable: true
                        enum: [processing, completed, failed, queued, idle, approval_required]
                        example: "idle"
                      agent_name:
                        type: string
                        nullable: true
                        example: "Sales research agent"
                      agent_team_id:
                        type: string
                        nullable: true
                        example: "team_4f8c92ab"
                      agent_creator_user_id:
                        type: string
                        nullable: true
                        example: "user_2b9d71f0"
                      agent_icon_url:
                        type: string
                        nullable: true
                        example: null
                      agent_tools:
                        type: array
                        description: Tools available to the agent. Secret references are stripped before being returned.
                        items:
                          type: object
                      participants:
                        type: object
                        description: Map of participant user IDs to participant metadata.
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the session. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      usage:
                        type: object
                        description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                        properties:
                          credit_cost:
                            type: number
                            nullable: true
                            description: Total credits consumed by the session, including tool and flow (workflow) credits.
                            example: 12.5
                          tool_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by tool calls.
                            example: 4
                          flow_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by workflow (flow) runs invoked by the agent.
                            example: 1.5
                          input_tokens:
                            type: integer
                            nullable: true
                            description: Total input tokens across the session.
                            example: 18432
                          cached_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                            example: 12288
                          cache_write_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                            example: 2048
                          output_tokens:
                            type: integer
                            nullable: true
                            description: Total output tokens across the session.
                            example: 2043
                          reasoning_tokens:
                            type: integer
                            nullable: true
                            description: Total reasoning (thinking) tokens across the session.
                            example: 512
                  queue_position:
                    type: integer
                    nullable: true
                    description: Position in the per-agent queue. Populated only when the session was queued; otherwise `null`.
                    example: null
              examples:
                idle:
                  summary: Idle session created (no input)
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages: []
                      created_at: "2026-05-15T14:32:00Z"
                      state: "idle"
                      agent_name: "Sales research agent"
                      agent_team_id: "team_4f8c92ab"
                      agent_creator_user_id: "user_2b9d71f0"
                      agent_icon_url: null
                      agent_tools: []
                      participants: {}
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                      usage:
                        credit_cost: null
                        tool_credit_cost: null
                        flow_credit_cost: null
                        input_tokens: null
                        cached_input_tokens: null
                        cache_write_input_tokens: null
                        output_tokens: null
                        reasoning_tokens: null
                    queue_position: null
        '202':
          description: Session created and the first message was enqueued. Returned when `input` is provided. `queue_position` is set only when the session was queued behind concurrent runs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      messages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              nullable: true
                              example: "msg_a1b2c3"
                            role:
                              type: string
                              nullable: true
                              example: "user"
                            content:
                              type: string
                              nullable: true
                              example: "Research Acme Corp and draft a brief."
                            created_at:
                              type: string
                              format: date-time
                              nullable: true
                              example: "2026-05-15T14:32:00Z"
                            creator_id:
                              type: string
                              nullable: true
                              example: "user_2b9d71f0"
                            parts:
                              type: array
                              nullable: true
                              items:
                                type: object
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-15T14:32:00Z"
                      state:
                        type: string
                        nullable: true
                        enum: [processing, completed, failed, queued, idle, approval_required]
                        example: "processing"
                      agent_name:
                        type: string
                        nullable: true
                        example: "Sales research agent"
                      agent_team_id:
                        type: string
                        nullable: true
                        example: "team_4f8c92ab"
                      agent_creator_user_id:
                        type: string
                        nullable: true
                        example: "user_2b9d71f0"
                      agent_icon_url:
                        type: string
                        nullable: true
                        example: null
                      agent_tools:
                        type: array
                        description: Tools available to the agent. Secret references are stripped before being returned.
                        items:
                          type: object
                      participants:
                        type: object
                        description: Map of participant user IDs to participant metadata.
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the session. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      usage:
                        type: object
                        description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                        properties:
                          credit_cost:
                            type: number
                            nullable: true
                            description: Total credits consumed by the session, including tool and flow (workflow) credits.
                            example: 12.5
                          tool_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by tool calls.
                            example: 4
                          flow_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by workflow (flow) runs invoked by the agent.
                            example: 1.5
                          input_tokens:
                            type: integer
                            nullable: true
                            description: Total input tokens across the session.
                            example: 18432
                          cached_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                            example: 12288
                          cache_write_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                            example: 2048
                          output_tokens:
                            type: integer
                            nullable: true
                            description: Total output tokens across the session.
                            example: 2043
                          reasoning_tokens:
                            type: integer
                            nullable: true
                            description: Total reasoning (thinking) tokens across the session.
                            example: 512
                  queue_position:
                    type: integer
                    nullable: true
                    description: Position in the per-agent queue. Populated only when the session was queued; otherwise `null`.
                    example: null
              examples:
                processing:
                  summary: Message enqueued and processing
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages:
                        - id: "msg_a1b2c3"
                          role: "user"
                          content: "Research Acme Corp and draft a brief."
                          created_at: "2026-05-15T14:32:00Z"
                          creator_id: "user_2b9d71f0"
                          parts: null
                      created_at: "2026-05-15T14:32:00Z"
                      state: "processing"
                      agent_name: "Sales research agent"
                      agent_team_id: "team_4f8c92ab"
                      agent_creator_user_id: "user_2b9d71f0"
                      agent_icon_url: null
                      agent_tools: []
                      participants:
                        "user_2b9d71f0":
                          first_name: "Ada"
                          last_name: "Lovelace"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                    queue_position: null
                queued:
                  summary: Message queued behind concurrent runs
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages:
                        - id: "msg_a1b2c3"
                          role: "user"
                          content: "Research Acme Corp and draft a brief."
                          created_at: "2026-05-15T14:32:00Z"
                          creator_id: "user_2b9d71f0"
                          parts: null
                      created_at: "2026-05-15T14:32:00Z"
                      state: "queued"
                      agent_name: "Sales research agent"
                      agent_team_id: "team_4f8c92ab"
                      agent_creator_user_id: "user_2b9d71f0"
                      agent_icon_url: null
                      agent_tools: []
                      participants:
                        "user_2b9d71f0":
                          first_name: "Ada"
                          last_name: "Lovelace"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                    queue_position: 3
        '400':
          description: 'Bad request — invalid body (including a `name` that is empty or longer than 256 characters after trimming), or `stream: true` was set on this host (use the streaming host).'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the agent.
        '404':
          description: Agent not found.
        '409':
          description: Conflict — a session with the supplied `session_id` already exists.
        '429':
          description: Rate limited — the agent's concurrency limit was exceeded.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}:
    get:
      summary: Retrieve session
      description: Retrieve a session by ID, including its messages, current state, agent metadata, and participants.
      operationId: retrieveSession
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.sessions.retrieve("sess_xYz789AbCd")
            print(response.session.state)
            print(response.session.usage.credit_cost, response.session.usage.input_tokens)
            for message in response.session.messages:
                print(message.role, message.content)
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to retrieve.
      responses:
        '200':
          description: The session.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      messages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              nullable: true
                              example: "msg_a1b2c3"
                            role:
                              type: string
                              nullable: true
                              example: "user"
                            content:
                              type: string
                              nullable: true
                              example: "Research Acme Corp and draft a brief."
                            created_at:
                              type: string
                              format: date-time
                              nullable: true
                              example: "2026-05-15T14:32:00Z"
                            creator_id:
                              type: string
                              nullable: true
                              example: "user_2b9d71f0"
                            parts:
                              type: array
                              nullable: true
                              items:
                                type: object
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-15T14:32:00Z"
                      state:
                        type: string
                        nullable: true
                        enum: [processing, completed, failed, queued, idle, approval_required]
                        example: "completed"
                      agent_name:
                        type: string
                        nullable: true
                        example: "Sales research agent"
                      agent_team_id:
                        type: string
                        nullable: true
                        example: "team_4f8c92ab"
                      agent_creator_user_id:
                        type: string
                        nullable: true
                        example: "user_2b9d71f0"
                      agent_icon_url:
                        type: string
                        nullable: true
                        example: null
                      agent_tools:
                        type: array
                        description: Tools available to the agent. Secret references are stripped before being returned.
                        items:
                          type: object
                      participants:
                        type: object
                        description: Map of participant user IDs to participant metadata.
                      pending_approvals:
                        type: array
                        description: Pending asks (tool approvals, human input requests, checkpoints) when the session is `approval_required`. Empty when nothing is pending. Answer them via the Resolve approvals endpoint.
                        items:
                          type: object
                          properties:
                            action_request_id:
                              type: string
                              nullable: true
                              description: ID to pass back in `approval_responses` when resolving.
                              example: "areq_9f3k2m"
                            type:
                              type: string
                              description: Kind of ask, e.g. `tool_approval` or `human_input`.
                              example: "tool_approval"
                            title:
                              type: string
                              nullable: true
                            reason:
                              type: string
                              nullable: true
                            recipient_user_id:
                              type: string
                              nullable: true
                            tool_name:
                              type: string
                              nullable: true
                              example: "send_email"
                            server_label:
                              type: string
                              nullable: true
                              example: "Gmail"
                            display_fields:
                              type: array
                              nullable: true
                              description: Label/value pairs describing the pending action.
                              items:
                                type: array
                                items:
                                  type: string
                            questions:
                              type: array
                              nullable: true
                              description: For `human_input` asks, the questions to answer via `response.values`.
                              items:
                                type: object
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the session. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      usage:
                        type: object
                        description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                        properties:
                          credit_cost:
                            type: number
                            nullable: true
                            description: Total credits consumed by the session, including tool and flow (workflow) credits.
                            example: 12.5
                          tool_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by tool calls.
                            example: 4
                          flow_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by workflow (flow) runs invoked by the agent.
                            example: 1.5
                          input_tokens:
                            type: integer
                            nullable: true
                            description: Total input tokens across the session.
                            example: 18432
                          cached_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                            example: 12288
                          cache_write_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                            example: 2048
                          output_tokens:
                            type: integer
                            nullable: true
                            description: Total output tokens across the session.
                            example: 2043
                          reasoning_tokens:
                            type: integer
                            nullable: true
                            description: Total reasoning (thinking) tokens across the session.
                            example: 512
                  queue_position:
                    type: integer
                    nullable: true
                    description: Position in the per-agent queue. Populated only when the session is currently queued; otherwise `null`.
                    example: null
              examples:
                completed:
                  summary: Completed session
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages:
                        - id: "msg_a1b2c3"
                          role: "user"
                          content: "Research Acme Corp and draft a brief."
                          created_at: "2026-05-15T14:32:00Z"
                          creator_id: "user_2b9d71f0"
                          parts: null
                        - id: "msg_d4e5f6"
                          role: "assistant"
                          content: "Here is what I found about Acme Corp..."
                          created_at: "2026-05-15T14:32:09Z"
                          creator_id: null
                          parts: null
                      created_at: "2026-05-15T14:32:00Z"
                      state: "completed"
                      agent_name: "Sales research agent"
                      agent_team_id: "team_4f8c92ab"
                      agent_creator_user_id: "user_2b9d71f0"
                      agent_icon_url: null
                      agent_tools: []
                      participants:
                        "user_2b9d71f0":
                          first_name: "Ada"
                          last_name: "Lovelace"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                      usage:
                        credit_cost: 12.5
                        tool_credit_cost: 4
                        flow_credit_cost: 1.5
                        input_tokens: 18432
                        cached_input_tokens: 12288
                        cache_write_input_tokens: 2048
                        output_tokens: 2043
                        reasoning_tokens: 512
                    queue_position: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the session.
        '404':
          description: Session not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    patch:
      summary: Rename session
      description: |
        Rename a session. `name` is the only mutable field; it is trimmed and must be between 1 and 256 characters after trimming.

        Returns the full session, in the same shape as Retrieve session.
      operationId: renameSession
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"name": "Acme Corp research"}'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to rename.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 256
                  description: New name for the session. Leading and trailing whitespace is removed before the 1-256 character limit is applied, so a whitespace-only value is rejected.
                  example: "Acme Corp research"
      responses:
        '200':
          description: The renamed session.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      name:
                        type: string
                        example: "Acme Corp research"
                      state:
                        type: string
                        enum: [idle, queued, processing, approval_required, completed, failed]
                        example: "completed"
                    description: Full session object; see Retrieve session for every field.
                  queue_position:
                    type: integer
                    nullable: true
                    example: null
              example:
                session:
                  id: "sess_xYz789AbCd"
                  agent_id: "abc123DEFghiJKL"
                  name: "Acme Corp research"
                  state: "completed"
                queue_position: null
        '400':
          description: Invalid request body — `name` is missing, empty, longer than 256 characters, or the body contains unknown fields.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}/messages:
    post:
      summary: Send message
      description: |
        Append a user message to an existing session and resume the agent. The session must be `idle`, `completed`, `failed`, or `approval_required`; sending to a session that is `processing` or `queued` returns `409 interaction_not_in_terminal_state`. To hand the agent a message while it is still busy, use the [message queue](/api-reference/sessions/queue-message) instead.

        Files uploaded via [Upload session file](/api-reference/sessions/upload-session-file) can be attached to the message with `attachments`.

        ### Sessions waiting on an approval

        A session that stopped to ask you something is `approval_required`, and you have two ways to move it forward:

        - **Answer the ask.** Send the pending asks' responses to [Resolve approvals](/api-reference/sessions/resolve-approvals). Use this to approve or reject a tool call, or to answer an Ask Question the agent raised. This endpoint rejects `approval_responses` with a `400`.
        - **Send a follow-up instead.** Post a normal message here. It is appended to the session transcript and starts a new turn, leaving the pending ask unanswered. Use this when the answer no longer matters — for example to redirect the agent or drop the request it was asking about.

        See [Human in the Loop](/core-concepts/human_in_the_loop) for how agents pause for approvals and questions.

        ### Streaming the response

        `api.gumloop.com` only serves the non-streaming response above. To stream agent output as it's produced, send the same request body (with `stream: true`) to the streaming host instead:

        `POST https://ws.gumloop.com/api/v1/sessions/{session_id}/messages`

        The response is `text/event-stream` (Server-Sent Events). With the Python SDK, `client.sessions.stream_message(session_id, input="...")` routes to `ws.gumloop.com` automatically and yields parsed `StreamEvent` objects.

        If you send `stream: true` to `api.gumloop.com` by mistake, the response is a `400` whose body contains the correct streaming host so you can retry against it.
      operationId: sendMessage
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/messages' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"input": "Now write a follow-up email."}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.sessions.send(
                "sess_xYz789AbCd",
                input="Now write a follow-up email.",
            )
            print(response.session.state)
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to continue.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                input:
                  type: string
                  description: The next user message. Required. Also accepted as `message` for backwards compatibility.
                  example: "Now write a follow-up email."
                stream:
                  type: boolean
                  default: false
                  description: Must be `false` (or omitted) when calling `api.gumloop.com`. Set to `true` only when calling `ws.gumloop.com` (see the streaming section above).
                attachments:
                  type: array
                  maxItems: 10
                  description: Files to attach to the message. Each `file_name` must be a stored path returned by Upload session file for this session.
                  items:
                    type: object
                    required:
                      - file_name
                    properties:
                      file_name:
                        type: string
                        description: Stored path returned by Upload session file.
                        example: "custom_agent_interactions/sess_xYz789AbCd/input/report.pdf"
                      media_type:
                        type: string
                        nullable: true
                        description: MIME type of the file.
                        example: "application/pdf"
              required:
                - input
      responses:
        '202':
          description: Message was enqueued. `queue_position` is set only when the session was queued behind concurrent runs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      messages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              nullable: true
                              example: "msg_g7h8i9"
                            role:
                              type: string
                              nullable: true
                              example: "user"
                            content:
                              type: string
                              nullable: true
                              example: "Now write a follow-up email."
                            created_at:
                              type: string
                              format: date-time
                              nullable: true
                              example: "2026-05-15T14:35:00Z"
                            creator_id:
                              type: string
                              nullable: true
                              example: "user_2b9d71f0"
                            parts:
                              type: array
                              nullable: true
                              items:
                                type: object
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-15T14:32:00Z"
                      state:
                        type: string
                        nullable: true
                        enum: [processing, completed, failed, queued, idle, approval_required]
                        example: "processing"
                      agent_name:
                        type: string
                        nullable: true
                        example: "Sales research agent"
                      agent_team_id:
                        type: string
                        nullable: true
                        example: "team_4f8c92ab"
                      agent_creator_user_id:
                        type: string
                        nullable: true
                        example: "user_2b9d71f0"
                      agent_icon_url:
                        type: string
                        nullable: true
                        example: null
                      agent_tools:
                        type: array
                        description: Tools available to the agent. Secret references are stripped before being returned.
                        items:
                          type: object
                      participants:
                        type: object
                        description: Map of participant user IDs to participant metadata.
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the session. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      usage:
                        type: object
                        description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                        properties:
                          credit_cost:
                            type: number
                            nullable: true
                            description: Total credits consumed by the session, including tool and flow (workflow) credits.
                            example: 12.5
                          tool_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by tool calls.
                            example: 4
                          flow_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by workflow (flow) runs invoked by the agent.
                            example: 1.5
                          input_tokens:
                            type: integer
                            nullable: true
                            description: Total input tokens across the session.
                            example: 18432
                          cached_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                            example: 12288
                          cache_write_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                            example: 2048
                          output_tokens:
                            type: integer
                            nullable: true
                            description: Total output tokens across the session.
                            example: 2043
                          reasoning_tokens:
                            type: integer
                            nullable: true
                            description: Total reasoning (thinking) tokens across the session.
                            example: 512
                  queue_position:
                    type: integer
                    nullable: true
                    description: Position in the per-agent queue. Populated only when the session was queued; otherwise `null`.
                    example: null
              examples:
                processing:
                  summary: Continuation enqueued and processing
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages:
                        - id: "msg_a1b2c3"
                          role: "user"
                          content: "Research Acme Corp and draft a brief."
                          created_at: "2026-05-15T14:32:00Z"
                          creator_id: "user_2b9d71f0"
                          parts: null
                        - id: "msg_d4e5f6"
                          role: "assistant"
                          content: "Here is what I found about Acme Corp..."
                          created_at: "2026-05-15T14:32:09Z"
                          creator_id: null
                          parts: null
                        - id: "msg_g7h8i9"
                          role: "user"
                          content: "Now write a follow-up email."
                          created_at: "2026-05-15T14:35:00Z"
                          creator_id: "user_2b9d71f0"
                          parts: null
                      created_at: "2026-05-15T14:32:00Z"
                      state: "processing"
                      agent_name: "Sales research agent"
                      agent_team_id: "team_4f8c92ab"
                      agent_creator_user_id: "user_2b9d71f0"
                      agent_icon_url: null
                      agent_tools: []
                      participants:
                        "user_2b9d71f0":
                          first_name: "Ada"
                          last_name: "Lovelace"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
                    queue_position: null
        '400':
          description: 'Bad request — missing `input`, `stream: true` was set on this host (use the streaming host), `approval_responses` was included (`approvals_require_stream_or_approvals_endpoint` — use the approvals endpoint), or an attachment path is outside this session''s input namespace (`invalid_attachment`).'
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '409':
          description: Conflict — the session is still running (`interaction_not_in_terminal_state`); it is `queued` or `processing`.
        '429':
          description: Rate limited — the agent's concurrency limit was exceeded.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}/cancel:
    post:
      summary: Cancel session
      description: |
        Cancel an in-progress session. If the session is currently `processing` or `queued`, any running stream is aborted and the session is transitioned to `failed`. If the session is already `completed` or `failed`, its current state is returned unchanged.

        The response carries a `session` envelope but only `id`, `agent_id`, and `state` are populated.
      operationId: cancelSession
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/cancel' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.sessions.cancel("sess_xYz789AbCd")
            print(response.session.state)
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to cancel.
      responses:
        '200':
          description: Session state after the cancel attempt. Only `id`, `agent_id`, and `state` are populated on the session envelope.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        example: "sess_xYz789AbCd"
                      agent_id:
                        type: string
                        example: "abc123DEFghiJKL"
                      messages:
                        type: array
                        items:
                          type: object
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: null
                      state:
                        type: string
                        nullable: true
                        enum: [processing, completed, failed, queued, idle, approval_required]
                        example: "failed"
                      agent_name:
                        type: string
                        nullable: true
                        example: null
                      agent_team_id:
                        type: string
                        nullable: true
                        example: null
                      agent_creator_user_id:
                        type: string
                        nullable: true
                        example: null
                      agent_icon_url:
                        type: string
                        nullable: true
                        example: null
                      agent_tools:
                        type: array
                        items:
                          type: object
                      participants:
                        type: object
                      creator:
                        type: object
                        nullable: true
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
                      usage:
                        type: object
                        description: Per-session usage totals. Credit and token counts accumulate as the agent runs and are `null` until the first run records usage.
                        properties:
                          credit_cost:
                            type: number
                            nullable: true
                            description: Total credits consumed by the session, including tool and flow (workflow) credits.
                            example: 12.5
                          tool_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by tool calls.
                            example: 4
                          flow_credit_cost:
                            type: number
                            nullable: true
                            description: Credits consumed by workflow (flow) runs invoked by the agent.
                            example: 1.5
                          input_tokens:
                            type: integer
                            nullable: true
                            description: Total input tokens across the session.
                            example: 18432
                          cached_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens read from the provider's prompt cache. Included in `input_tokens` and billed at the cache-read rate.
                            example: 12288
                          cache_write_input_tokens:
                            type: integer
                            nullable: true
                            description: Input tokens written to the provider's prompt cache. Included in `input_tokens` and billed at the cache-write rate.
                            example: 2048
                          output_tokens:
                            type: integer
                            nullable: true
                            description: Total output tokens across the session.
                            example: 2043
                          reasoning_tokens:
                            type: integer
                            nullable: true
                            description: Total reasoning (thinking) tokens across the session.
                            example: 512
                  queue_position:
                    type: integer
                    nullable: true
                    example: null
              examples:
                cancelled:
                  summary: Cancelled an in-progress session
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages: []
                      created_at: null
                      state: "failed"
                      agent_name: null
                      agent_team_id: null
                      agent_creator_user_id: null
                      agent_icon_url: null
                      agent_tools: []
                      participants: {}
                      creator: null
                    queue_position: null
                already_completed:
                  summary: Already in a terminal state — current state returned
                  value:
                    session:
                      id: "sess_xYz789AbCd"
                      agent_id: "abc123DEFghiJKL"
                      messages: []
                      created_at: null
                      state: "completed"
                      agent_name: null
                      agent_team_id: null
                      agent_creator_user_id: null
                      agent_icon_url: null
                      agent_tools: []
                      participants: {}
                      creator: null
                    queue_position: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '500':
          description: Internal server error — the abort could not be persisted (`failed_to_abort`).
      security:
        - bearerAuth: []

  /sessions/{session_id}/files:
    post:
      summary: Upload session file
      description: |
        Upload a file into a session's input namespace so it can be attached to a message. The response returns the stored path — pass it as `file_name` in the `attachments` array when sending a message on the same session.

        Files are base64 encoded in the request body and limited to 200MB (decoded). Uploaded files are scoped to the session they were uploaded to and cannot be attached to messages on other sessions.
      operationId: uploadSessionFile
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/files' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "file_name": "report.pdf",
                "file_content": "'"$(base64 -w 0 report.pdf)"'",
                "media_type": "application/pdf"
              }'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to upload the file to.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - file_name
                - file_content
              properties:
                file_name:
                  type: string
                  description: Name of the file. Directory components are stripped; the base name is sanitized before storage.
                  example: "report.pdf"
                file_content:
                  type: string
                  format: byte
                  description: Base64-encoded file contents. Maximum decoded size is 200MB.
                media_type:
                  type: string
                  description: MIME type of the file. Echoed back in the response.
                  example: "application/pdf"
      responses:
        '201':
          description: File stored. Use `file_name` from the response as an attachment on this session's messages.
          content:
            application/json:
              schema:
                type: object
                properties:
                  file_name:
                    type: string
                    description: Stored path of the file inside the session's input namespace.
                    example: "custom_agent_interactions/sess_xYz789AbCd/input/report.pdf"
                  media_type:
                    type: string
                    nullable: true
                    example: "application/pdf"
                  size:
                    type: integer
                    description: Decoded file size in bytes.
                    example: 482133
              example:
                file_name: "custom_agent_interactions/sess_xYz789AbCd/input/report.pdf"
                media_type: "application/pdf"
                size: 482133
        '400':
          description: Bad request — empty or invalid `file_name` (`invalid_file_name`), or `file_content` is not valid base64 (`invalid_file_content`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '413':
          description: File too large — the file exceeds the 200MB limit (`file_too_large`).
        '500':
          description: Internal server error — the file could not be stored (`file_upload_failed`).
      security:
        - bearerAuth: []

  /sessions/{session_id}/approvals:
    post:
      summary: Resolve approvals
      description: |
        Answer pending asks on a session that is paused in the `approval_required` state — tool approvals, human input requests, and checkpoints.

        List the pending asks with Retrieve session: each entry in `pending_approvals` carries the `action_request_id` to answer, and `human_input` asks include the `questions` to fill in via `response.values`. Resolutions are processed in order; the agent resumes once the pending asks are answered.
      operationId: resolveSessionApprovals
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/approvals' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "approval_responses": [
                  {"action_request_id": "areq_9f3k2m", "action": "accept"}
                ]
              }'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session with pending approvals.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - approval_responses
              properties:
                approval_responses:
                  type: array
                  minItems: 1
                  maxItems: 20
                  description: Answers to pending asks. Each `action_request_id` may appear at most once.
                  items:
                    type: object
                    required:
                      - action_request_id
                      - action
                    properties:
                      action_request_id:
                        type: string
                        description: ID of the pending ask, from `pending_approvals` on Retrieve session.
                        example: "areq_9f3k2m"
                      action:
                        type: string
                        enum: [accept, reject]
                        description: Whether to approve or reject the pending ask.
                      reason:
                        type: string
                        maxLength: 1000
                        description: Optional reason recorded with the resolution.
                      response:
                        type: object
                        description: For `human_input` asks — form answers keyed by question name.
                        properties:
                          values:
                            type: object
                            description: Map of question name to answer.
                            example:
                              email_subject: "Q3 pipeline review"
      responses:
        '200':
          description: Resolutions applied. `results` reports the outcome per ask; `session` reflects the state after resolution.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    description: The session after resolution — same shape as Retrieve session.
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        action_request_id:
                          type: string
                          example: "areq_9f3k2m"
                        action:
                          type: string
                          enum: [accept, reject]
                          example: "accept"
                        outcome:
                          type: string
                          description: Resolution outcome code — `accepted` or `rejected` for fresh resolutions, `already_accepted` / `already_rejected` when the ask was already resolved the same way.
                          example: "accepted"
                  stream_cursor:
                    type: string
                    nullable: true
                    description: Cursor to resume the session's event stream from, when the resolution restarted the agent.
              example:
                session:
                  id: "sess_xYz789AbCd"
                  agent_id: "abc123DEFghiJKL"
                  state: "processing"
                  pending_approvals: []
                results:
                  - action_request_id: "areq_9f3k2m"
                    action: "accept"
                    outcome: "accepted"
                stream_cursor: null
        '400':
          description: Bad request — unknown or already-resolved `action_request_id`, a repeated `action_request_id`, or an invalid `human_input` answer.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '409':
          description: Conflict — the session has no pending approvals (`session_not_awaiting_approval`).
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}/queue:
    get:
      summary: List queued messages
      description: List the messages waiting in a session's queue, in the order they will be sent. Queued messages are drained automatically when the agent finishes its current turn.
      operationId: listQueuedMessages
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/queue' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session whose queue to list.
      responses:
        '200':
          description: The session's message queue.
          content:
            application/json:
              schema:
                type: object
                properties:
                  queue:
                    type: array
                    items:
                      $ref: '#/components/schemas/QueuedMessage'
              example:
                queue:
                  - id: "qmsg_1a2b3c"
                    input: "Also check their latest funding round."
                    state: "queued"
                    position: 1
                    created_at: "2026-05-15T14:36:00Z"
                    updated_at: "2026-05-15T14:36:00Z"
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the session.
        '404':
          description: Session not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    post:
      summary: Queue message
      description: |
        Add a message to a session's queue instead of interrupting the agent. Queued messages are sent automatically, in order, when the agent finishes its current turn. A session's queue holds at most 20 messages.

        To interrupt the current turn and send a queued message immediately, use [Send queued message now](/api-reference/sessions/send-queued-message).
      operationId: queueSessionMessage
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/queue' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"input": "Also check their latest funding round."}'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session to queue the message on.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - input
              properties:
                input:
                  type: string
                  description: The message to queue. Cannot be empty. Also accepted as `message` for backwards compatibility.
                  example: "Also check their latest funding round."
      responses:
        '201':
          description: Message queued. `queued_message` is the new entry; `queue` is the rest of the queue after it.
          content:
            application/json:
              schema:
                type: object
                properties:
                  queued_message:
                    $ref: '#/components/schemas/QueuedMessage'
                  queue:
                    type: array
                    items:
                      $ref: '#/components/schemas/QueuedMessage'
              example:
                queued_message:
                  id: "qmsg_1a2b3c"
                  input: "Also check their latest funding round."
                  state: "queued"
                  position: 1
                  created_at: "2026-05-15T14:36:00Z"
                  updated_at: "2026-05-15T14:36:00Z"
                queue: []
        '400':
          description: Bad request — empty `input`.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session not found.
        '409':
          description: Conflict — the queue already holds 20 messages (`queue_full`).
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}/queue/{queued_message_id}:
    patch:
      summary: Update queued message
      description: Replace the content of a message that is still waiting in the session's queue.
      operationId: updateQueuedMessage
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/queue/qmsg_1a2b3c' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"input": "Also check their latest funding round and headcount."}'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session the queued message belongs to.
        - in: path
          name: queued_message_id
          required: true
          schema:
            type: string
          description: ID of the queued message to update.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - input
              properties:
                input:
                  type: string
                  description: The new message content. Cannot be empty. Also accepted as `message` for backwards compatibility.
                  example: "Also check their latest funding round and headcount."
      responses:
        '200':
          description: Queued message updated. `queued_message` is the updated entry; `queue` is the rest of the queue.
          content:
            application/json:
              schema:
                type: object
                properties:
                  queued_message:
                    $ref: '#/components/schemas/QueuedMessage'
                  queue:
                    type: array
                    items:
                      $ref: '#/components/schemas/QueuedMessage'
              example:
                queued_message:
                  id: "qmsg_1a2b3c"
                  input: "Also check their latest funding round and headcount."
                  state: "queued"
                  position: 1
                  created_at: "2026-05-15T14:36:00Z"
                  updated_at: "2026-05-15T14:38:00Z"
                queue: []
        '400':
          description: Bad request — empty `input`.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session or queued message not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    delete:
      summary: Delete queued message
      description: Remove a message from the session's queue before it is sent.
      operationId: deleteQueuedMessage
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/queue/qmsg_1a2b3c' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session the queued message belongs to.
        - in: path
          name: queued_message_id
          required: true
          schema:
            type: string
          description: ID of the queued message to delete.
      responses:
        '200':
          description: Queued message removed. `queue` is the remaining queue.
          content:
            application/json:
              schema:
                type: object
                properties:
                  queue:
                    type: array
                    items:
                      $ref: '#/components/schemas/QueuedMessage'
              example:
                queue: []
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session or queued message not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /sessions/{session_id}/queue/{queued_message_id}/send:
    post:
      summary: Send queued message now
      description: |
        Send a queued message immediately instead of waiting for the agent to finish its current turn. Any in-progress run is aborted, the queued message is appended to the transcript, and the agent starts processing it.

        The response is the same envelope as Send message. Queued messages cannot be sent this way on incognito sessions, and a message that is currently being edited must have its edit finished or cancelled first.
      operationId: sendQueuedMessage
      tags:
        - Sessions
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/sessions/sess_xYz789AbCd/queue/qmsg_1a2b3c/send' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
      parameters:
        - in: path
          name: session_id
          required: true
          schema:
            type: string
          description: ID of the session the queued message belongs to.
        - in: path
          name: queued_message_id
          required: true
          schema:
            type: string
          description: ID of the queued message to send.
      responses:
        '202':
          description: Message sent and processing started. Same envelope as Send message — `session` plus `queue_position`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    description: The session after the message was promoted — same shape as Retrieve session.
                  queue_position:
                    type: integer
                    nullable: true
                    description: Position in the per-agent queue. Populated only when the session was queued behind concurrent runs; otherwise `null`.
              example:
                session:
                  id: "sess_xYz789AbCd"
                  agent_id: "abc123DEFghiJKL"
                  state: "processing"
                queue_position: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have update access on the session.
        '404':
          description: Session, agent, or queued message not found.
        '409':
          description: Conflict — the session is incognito (`incognito_session_unsupported`), or the queued message is mid-edit (`queued_message_not_eligible`).
        '429':
          description: Rate limited — the agent's concurrency limit was exceeded.
        '500':
          description: Internal server error — the promoted message could not be persisted (`promoted_message_persist_failed`).
      security:
        - bearerAuth: []
  /mcp/servers:
    get:
      summary: List MCP servers
      description: Return the catalog of MCP servers visible to the caller — Gumloop-hosted (`gumcp_server`), user-deployed Gumstack (`gumstack_server`), and custom (`mcp_server`) — along with each server's connection state.
      operationId: listMcpServers
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.list_servers(team_id="YOUR_TEAM_ID")
            for server in response.servers:
                print(server.server_id, server.status)
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the catalog to a single team. When omitted, returns servers visible to the authenticated user.
      responses:
        '200':
          description: MCP servers visible to the caller.
          content:
            application/json:
              schema:
                type: object
                properties:
                  servers:
                    type: array
                    items:
                      type: object
                      required:
                        - server_id
                        - type
                        - status
                        - gumloop_auth_url
                      properties:
                        server_id:
                          type: string
                          description: Stable identifier for the server.
                          example: "gumloop_slack"
                        name:
                          type: string
                          nullable: true
                          example: "Slack"
                        type:
                          type: string
                          description: One of `gumcp_server`, `gumstack_server`, or `mcp_server`.
                          example: "gumcp_server"
                        status:
                          type: string
                          description: Connection state. `connected` means the server is ready to accept tool calls; other values (for example `unauthenticated`, `blocked`) indicate the user must complete OAuth or the server is otherwise unavailable.
                          example: "connected"
                        icon_url:
                          type: string
                          nullable: true
                          example: "https://www.gumloop.com/icons/slack.png"
                        description:
                          type: string
                          nullable: true
                          example: "Send and read Slack messages."
                        gumloop_auth_url:
                          type: string
                          description: URL the user should visit to connect or reauthorize this server.
                          example: "https://www.gumloop.com/oauth/connect/slack"
                        mcp_url:
                          type: string
                          nullable: true
                          description: For `mcp_server` (custom) and `gumstack_server` entries, the upstream MCP URL. `null` for Gumloop-hosted servers.
                          example: null
                        tool_count:
                          type: integer
                          nullable: true
                          example: 12
                        allowed_tool_call_ids:
                          type: array
                          nullable: true
                          description: Populated only by the retrieve endpoint. Always `null` here.
                          items:
                            type: string
                          example: null
              examples:
                multiple:
                  summary: Mixed connection states
                  value:
                    servers:
                      - server_id: "gumloop_slack"
                        name: "Slack"
                        type: "gumcp_server"
                        status: "connected"
                        icon_url: "https://www.gumloop.com/icons/slack.png"
                        description: "Send and read Slack messages."
                        gumloop_auth_url: "https://www.gumloop.com/oauth/connect/slack"
                        mcp_url: null
                        tool_count: 12
                        allowed_tool_call_ids: null
                      - server_id: "gumloop_linear"
                        name: "Linear"
                        type: "gumcp_server"
                        status: "unauthenticated"
                        icon_url: "https://www.gumloop.com/icons/linear.png"
                        description: "Read and create Linear issues."
                        gumloop_auth_url: "https://www.gumloop.com/oauth/connect/linear"
                        mcp_url: null
                        tool_count: 8
                        allowed_tool_call_ids: null
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have agent access on the requested team.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}:
    get:
      summary: Retrieve an MCP server
      description: Return a single MCP server. The response populates `allowed_tool_call_ids` with the tool call IDs the caller is permitted to invoke on this server.
      operationId: retrieveMcpServer
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_slack?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.get_server("gumloop_slack", team_id="YOUR_TEAM_ID")
            print(response.server.status, response.server.allowed_tool_call_ids)
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
          description: Identifier of the MCP server to retrieve.
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the lookup to a single team.
      responses:
        '200':
          description: The requested MCP server.
          content:
            application/json:
              schema:
                type: object
                properties:
                  server:
                    type: object
                    required:
                      - server_id
                      - type
                      - status
                      - gumloop_auth_url
                    properties:
                      server_id:
                        type: string
                        example: "gumloop_slack"
                      name:
                        type: string
                        nullable: true
                        example: "Slack"
                      type:
                        type: string
                        description: One of `gumcp_server`, `gumstack_server`, or `mcp_server`.
                        example: "gumcp_server"
                      status:
                        type: string
                        example: "connected"
                      icon_url:
                        type: string
                        nullable: true
                        example: "https://www.gumloop.com/icons/slack.png"
                      description:
                        type: string
                        nullable: true
                        example: "Send and read Slack messages."
                      gumloop_auth_url:
                        type: string
                        example: "https://www.gumloop.com/oauth/connect/slack"
                      mcp_url:
                        type: string
                        nullable: true
                        example: null
                      tool_count:
                        type: integer
                        nullable: true
                        example: 12
                      allowed_tool_call_ids:
                        type: array
                        nullable: true
                        description: Tool call IDs the caller is permitted to invoke on this server, after RBAC and policy checks.
                        items:
                          type: string
                        example:
                          - "gumloop_slack__slack_send_message"
                          - "gumloop_slack__slack_list_channels"
              examples:
                connected:
                  summary: Connected server
                  value:
                    server:
                      server_id: "gumloop_slack"
                      name: "Slack"
                      type: "gumcp_server"
                      status: "connected"
                      icon_url: "https://www.gumloop.com/icons/slack.png"
                      description: "Send and read Slack messages."
                      gumloop_auth_url: "https://www.gumloop.com/oauth/connect/slack"
                      mcp_url: null
                      tool_count: 12
                      allowed_tool_call_ids:
                        - "gumloop_slack__slack_send_message"
                        - "gumloop_slack__slack_list_channels"
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have agent access on the requested team.
        '404':
          description: MCP server not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}/tools:
    get:
      summary: List MCP server tools
      description: Return the tools exposed by an MCP server. When the server is not in `connected` state, `tools` is empty and `gumloop_auth_url` is returned so the caller can prompt the user to authenticate.
      operationId: listMcpServerTools
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_slack/tools?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.list_tools("gumloop_slack", team_id="YOUR_TEAM_ID")
            for tool in response.tools:
                print(tool.tool_call_id, tool.name)
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
          description: Identifier of the MCP server.
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the lookup to a single team.
      responses:
        '200':
          description: Tools available on the server, plus the server's connection state.
          content:
            application/json:
              schema:
                type: object
                properties:
                  tools:
                    type: array
                    items:
                      type: object
                      required:
                        - tool_call_id
                        - name
                      properties:
                        tool_call_id:
                          type: string
                          description: Composite identifier used to route tool calls — `"{server_id}__{tool_name}"`. Pass `server_id` and `tool_name` separately to `POST /mcp/tools/call`.
                          example: "gumloop_slack__slack_send_message"
                        name:
                          type: string
                          example: "slack_send_message"
                        description:
                          type: string
                          nullable: true
                          example: "Send a message to a Slack channel."
                        input_schema:
                          type: object
                          description: JSON Schema describing the tool's arguments. Defaults to `{}` when the upstream server does not advertise a schema.
                          example:
                            type: object
                            properties:
                              channel:
                                type: string
                              text:
                                type: string
                            required:
                              - channel
                              - text
                        server_id:
                          type: string
                          nullable: true
                          example: "gumloop_slack"
                        server_type:
                          type: string
                          nullable: true
                          example: "gumcp_server"
                        server:
                          type: object
                          description: Server metadata snapshot. Defaults to `{}`.
                          example: {}
                  server_id:
                    type: string
                    nullable: true
                    example: "gumloop_slack"
                  status:
                    type: string
                    nullable: true
                    example: "connected"
                  gumloop_auth_url:
                    type: string
                    nullable: true
                    example: "https://www.gumloop.com/oauth/connect/slack"
              examples:
                connected:
                  summary: Connected server
                  value:
                    tools:
                      - tool_call_id: "gumloop_slack__slack_send_message"
                        name: "slack_send_message"
                        description: "Send a message to a Slack channel."
                        input_schema:
                          type: object
                          properties:
                            channel:
                              type: string
                            text:
                              type: string
                          required:
                            - channel
                            - text
                        server_id: "gumloop_slack"
                        server_type: "gumcp_server"
                        server: {}
                    server_id: "gumloop_slack"
                    status: "connected"
                    gumloop_auth_url: "https://www.gumloop.com/oauth/connect/slack"
                unauthenticated:
                  summary: Server not yet connected
                  value:
                    tools: []
                    server_id: "gumloop_slack"
                    status: "unauthenticated"
                    gumloop_auth_url: "https://www.gumloop.com/oauth/connect/slack"
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have agent access on the requested team.
        '404':
          description: MCP server not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}/resources:
    get:
      summary: List MCP server resources
      description: Return the resources an MCP server exposes, fetched live from the server. When the server is not `connected`, `resources` is empty and `gumloop_auth_url` is returned so the caller can prompt the user to authenticate.
      operationId: listMcpServerResources
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_notion/resources?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.list_resources("gumloop_notion", team_id="YOUR_TEAM_ID")
            for resource in response.resources:
                print(resource.uri, resource.name, resource.mime_type)
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
          description: Identifier of the MCP server.
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the lookup to a single team.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque cursor from a previous response's `next_cursor`.
      responses:
        '200':
          description: Resources available on the server, plus the server's connection state.
          content:
            application/json:
              schema:
                type: object
                required: [resources]
                properties:
                  resources:
                    type: array
                    items:
                      type: object
                      required: [uri]
                      properties:
                        uri:
                          type: string
                          example: "notion://pages/2f1a9c7e"
                        name:
                          type: string
                          nullable: true
                        title:
                          type: string
                          nullable: true
                        description:
                          type: string
                          nullable: true
                        mime_type:
                          type: string
                          nullable: true
                          example: "text/markdown"
                        size:
                          type: integer
                          nullable: true
                        server_id:
                          type: string
                          nullable: true
                  server_id:
                    type: string
                    nullable: true
                  status:
                    type: string
                    nullable: true
                    description: The server's connection state, `connected` when resources could be fetched.
                  gumloop_auth_url:
                    type: string
                    nullable: true
                    description: Where to send the user to connect the server, when it is not connected.
                  next_cursor:
                    type: string
                    nullable: true
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot use agents in the team, or the server is disabled by organization policy.
        '404':
          description: Server not found.
        '502':
          description: The MCP server could not be reached or returned an error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}/resources/read:
    get:
      summary: Read MCP server resource
      description: Read one resource by `uri`. Each content item is either `text` or a base64 `blob`, never both.
      operationId: readMcpServerResource
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_notion/resources/read?uri=notion%3A%2F%2Fpages%2F2f1a9c7e&team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.get_resource("gumloop_notion", "notion://pages/2f1a9c7e", team_id="YOUR_TEAM_ID")
            for content in response.contents:
                print(content.mime_type, content.text or "<binary>")
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
        - in: query
          name: uri
          required: true
          schema:
            type: string
          description: The resource `uri` from List MCP server resources.
        - in: query
          name: team_id
          required: false
          schema:
            type: string
      responses:
        '200':
          description: The resource contents.
          content:
            application/json:
              schema:
                type: object
                required: [server_id, uri, contents]
                properties:
                  server_id:
                    type: string
                  uri:
                    type: string
                  contents:
                    type: array
                    items:
                      type: object
                      properties:
                        uri:
                          type: string
                          nullable: true
                        mime_type:
                          type: string
                          nullable: true
                        text:
                          type: string
                          nullable: true
                        blob:
                          type: string
                          nullable: true
                          description: Base64-encoded bytes for binary resources.
        '400':
          description: "`uri` is missing (`uri_required`)."
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot use agents in the team, or the server is disabled by organization policy.
        '404':
          description: Server not found.
        '409':
          description: The server is not connected.
        '502':
          description: The MCP server could not be reached or returned an error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}/prompts:
    get:
      summary: List MCP server prompts
      description: Return the prompt templates an MCP server exposes, fetched live from the server. When the server is not `connected`, `prompts` is empty and `gumloop_auth_url` is returned.
      operationId: listMcpServerPrompts
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_notion/prompts?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            for prompt in client.mcp.list_prompts("gumloop_notion", team_id="YOUR_TEAM_ID").prompts:
                print(prompt.name, [a.name for a in prompt.arguments])
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
        - in: query
          name: team_id
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Prompts available on the server, plus the server's connection state.
          content:
            application/json:
              schema:
                type: object
                required: [prompts]
                properties:
                  prompts:
                    type: array
                    items:
                      type: object
                      required: [name]
                      properties:
                        name:
                          type: string
                          example: "summarize_page"
                        description:
                          type: string
                          nullable: true
                        arguments:
                          type: array
                          items:
                            type: object
                            required: [name]
                            properties:
                              name:
                                type: string
                              description:
                                type: string
                                nullable: true
                              required:
                                type: boolean
                                nullable: true
                        server_id:
                          type: string
                          nullable: true
                  server_id:
                    type: string
                    nullable: true
                  status:
                    type: string
                    nullable: true
                  gumloop_auth_url:
                    type: string
                    nullable: true
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot use agents in the team, or the server is disabled by organization policy.
        '404':
          description: Server not found.
        '502':
          description: The MCP server could not be reached or returned an error.
      security:
        - bearerAuth: []

  /mcp/servers/{server_id}/prompts/get:
    post:
      summary: Get MCP server prompt
      description: Render one prompt template with arguments and return its messages.
      operationId: getMcpServerPrompt
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/servers/gumloop_notion/prompts/get' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"name": "summarize_page", "arguments": {"page_id": "2f1a9c7e"}, "team_id": "YOUR_TEAM_ID"}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.get_prompt("gumloop_notion", "summarize_page", {"page_id": "2f1a9c7e"}, team_id="YOUR_TEAM_ID")
            for message in response.messages:
                print(message.role, message.content)
      parameters:
        - in: path
          name: server_id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Prompt name from List MCP server prompts.
                arguments:
                  type: object
                  additionalProperties: true
                  description: Argument values for the template.
                team_id:
                  type: string
                  description: Scope the lookup to a single team.
      responses:
        '200':
          description: The rendered prompt.
          content:
            application/json:
              schema:
                type: object
                required: [server_id, name, messages]
                properties:
                  server_id:
                    type: string
                  name:
                    type: string
                  description:
                    type: string
                    nullable: true
                  messages:
                    type: array
                    items:
                      type: object
                      required: [role]
                      properties:
                        role:
                          type: string
                          example: "user"
                        content:
                          type: object
                          additionalProperties: true
                          description: 'An MCP content block, for example `{"type": "text", "text": "..."}`.'
        '400':
          description: Invalid request body.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot use agents in the team, or the server is disabled by organization policy.
        '404':
          description: Server not found.
        '409':
          description: The server is not connected.
        '502':
          description: The MCP server could not be reached or returned an error.
      security:
        - bearerAuth: []

  /mcp/tools/call:
    post:
      summary: Call MCP tools
      description: Execute a batch of 1–5 MCP tool calls. Calls run concurrently and each result reports its own `status`. When Gumloop accepts the request, MCP execution failures such as target server authentication, policy blocks, invalid tools, upstream HTTP errors, and connection failures are returned in `results[*].status` and `results[*].error`. Top-level `4xx` responses are reserved for Gumloop request, authentication, and permission failures. `200` covers homogeneous execution outcomes (all calls succeeded or all calls failed); mixed success/failure batches return `207`. If you previously treated non-2xx HTTP statuses as MCP execution failures, update your integration to inspect each result's `status` and `error`.
      operationId: callMcpTools
      tags:
        - MCP
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/mcp/tools/call' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "team_id": "team_4f8c92ab",
                "calls": [
                  {
                    "ref": "send-1",
                    "server_id": "gumloop_slack",
                    "tool_name": "slack_send_message",
                    "arguments": {"channel": "#general", "text": "Hello from Gumloop"}
                  }
                ]
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.mcp.execute(
                server_id="gumloop_slack",
                tool_name="slack_send_message",
                arguments={"channel": "#general", "text": "Hello from Gumloop"},
                ref="send-1",
                team_id="team_4f8c92ab",
            )
            for result in response.results:
                print(result.ref, result.status)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - calls
              properties:
                calls:
                  type: array
                  minItems: 1
                  maxItems: 5
                  description: Tool calls to execute. Dispatched concurrently; the batch is capped at 5.
                  items:
                    type: object
                    required:
                      - server_id
                      - tool_name
                    properties:
                      ref:
                        type: string
                        nullable: true
                        description: Caller-supplied identifier echoed back on the matching result. When omitted, Gumloop assigns the call's zero-based index in `calls` as its `ref`.
                        example: "send-1"
                      server_id:
                        type: string
                        example: "gumloop_slack"
                      tool_name:
                        type: string
                        example: "slack_send_message"
                      arguments:
                        type: object
                        description: Arguments passed to the tool. Defaults to `{}`. Validated by the tool's `input_schema`.
                        example:
                          channel: "#general"
                          text: "Hello from Gumloop"
                team_id:
                  type: string
                  nullable: true
                  description: Team the calls are scoped to.
                  example: "team_4f8c92ab"
      responses:
        '200':
          description: Batch processed. Inspect each result's `status` and `error`; this can include all-success and all-failed execution outcomes.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      required:
                        - ref
                        - status
                      properties:
                        ref:
                          type: string
                          example: "send-1"
                        server_id:
                          type: string
                          nullable: true
                          example: "gumloop_slack"
                        tool_name:
                          type: string
                          nullable: true
                          example: "slack_send_message"
                        status:
                          type: string
                          description: One of `success`, `unauthenticated`, or `error`.
                          example: "success"
                        content:
                          type: array
                          nullable: true
                          description: Raw MCP content blocks returned by the tool when `status` is `success`.
                          items:
                            type: object
                          example:
                            - type: "text"
                              text: "Message sent to #general"
                        error:
                          type: object
                          nullable: true
                          description: Error payload when `status` is not `success`. Includes `code`, `message`, `type`, and optional `param` and `details`.
                          example: null
              examples:
                success:
                  summary: Single successful call
                  value:
                    results:
                      - ref: "send-1"
                        server_id: "gumloop_slack"
                        tool_name: "slack_send_message"
                        status: "success"
                        content:
                          - type: "text"
                            text: "Message sent to #general"
                        error: null
                execution_error:
                  summary: Tool execution failed
                  value:
                    results:
                      - ref: "issue-1"
                        server_id: "gumloop_linear"
                        tool_name: "linear_create_issue"
                        status: "unauthenticated"
                        content: null
                        error:
                          code: "auth_required"
                          message: "Connect Linear before using this tool."
                          type: "permission_error"
                          param: "tool_name"
                          details:
                            server_id: "gumloop_linear"
                            tool_name: "linear_create_issue"
                            gumloop_auth_url: "https://www.gumloop.com/oauth/connect/linear"
        '207':
          description: Partial success — at least one call succeeded and at least one failed. Inspect each result's `status` and `error`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
              examples:
                mixed:
                  summary: One success, one needs authentication
                  value:
                    results:
                      - ref: "send-1"
                        server_id: "gumloop_slack"
                        tool_name: "slack_send_message"
                        status: "success"
                        content:
                          - type: "text"
                            text: "Message sent to #general"
                        error: null
                      - ref: "issue-1"
                        server_id: "gumloop_linear"
                        tool_name: "linear_create_issue"
                        status: "unauthenticated"
                        content: null
                        error:
                          code: "auth_required"
                          message: "Connect Linear before using this tool."
                          type: "permission_error"
                          param: "tool_name"
                          details:
                            server_id: "gumloop_linear"
                            tool_name: "linear_create_issue"
                            gumloop_auth_url: "https://www.gumloop.com/oauth/connect/linear"
        '400':
          description: Invalid Gumloop request body, for example fewer than 1 or more than 5 calls.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller lacks permission to use this Gumloop API endpoint or requested team scope.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /brain/search:
    post:
      summary: Search Company Brain
      description: |
        Run a hybrid (semantic + keyword) search across the knowledge sources indexed in your [Company Brain](/core-concepts/brain) and return the most relevant, ranked snippets with citations.

        Results are scoped to what the authenticated user can see: personal sources, plus any team and organization sources shared with them. Requires the Brain feature, which is available on the Pro and Enterprise plans. Each search consumes Gumloop credits.
      operationId: searchBrain
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/search' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "query": "what is our refund policy?",
                "limit": 8,
                "source_type": ["notion", "google_drive"]
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.brain.search(
                "what is our refund policy?",
                limit=8,
                source_type=["notion", "google_drive"],
            )
            for result in response.results:
                print(result.score, result.source, result.title, result.url)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                  description: The natural-language search query.
                  example: "what is our refund policy?"
                limit:
                  type: integer
                  minimum: 1
                  maximum: 50
                  default: 8
                  description: Maximum number of results to return.
                  example: 8
                source_type:
                  type: array
                  nullable: true
                  minItems: 1
                  description: |
                    Restrict results to specific source types. Omit to search every source you can access. Valid values: `notion`, `google_drive`, `slack`, `github`, `confluence`, `direct_file_uploads`, `gumloop_artifacts`.
                  items:
                    type: string
                    enum:
                      - notion
                      - google_drive
                      - slack
                      - github
                      - confluence
                      - direct_file_uploads
                      - gumloop_artifacts
                  example:
                    - notion
                    - google_drive
      responses:
        '200':
          description: Ranked search results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - results
                properties:
                  results:
                    type: array
                    description: Ranked matches, most relevant first.
                    items:
                      type: object
                      properties:
                        document_id:
                          type: string
                          nullable: true
                          description: Identifier of the matched document.
                        source:
                          type: string
                          nullable: true
                          description: The source type the result came from, for example `notion` or `slack`.
                        title:
                          type: string
                          nullable: true
                          description: Title of the matched document.
                        content:
                          type: string
                          nullable: true
                          description: The matching snippet of text.
                        url:
                          type: string
                          nullable: true
                          description: Link to the document in its original source, when available.
                        score:
                          type: number
                          nullable: true
                          description: Relevance score, normalized to a 0–1 range.
                        updated_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: When the document was last updated in its source.
                        owner_name:
                          type: string
                          nullable: true
                          description: Display name of the document owner, when known.
                        owner_email:
                          type: string
                          nullable: true
                          description: Email of the document owner, when known.
                        parent_title:
                          type: string
                          nullable: true
                          description: Title of the parent item (for example, a channel or folder), when applicable.
                        metadata:
                          type: object
                          description: Additional source-specific metadata.
              examples:
                success:
                  summary: A single ranked result
                  value:
                    results:
                      - document_id: "notion:2f1a9c7e"
                        source: "notion"
                        title: "Refund Policy"
                        content: "Customers may request a full refund within 30 days of purchase..."
                        url: "https://www.notion.so/Refund-Policy-2f1a9c7e"
                        score: 0.87
                        updated_at: "2024-01-15T10:30:00Z"
                        owner_name: "Jordan Lee"
                        owner_email: "jordan@example.com"
                        parent_title: "Policies"
                        metadata: {}
        '400':
          description: Invalid request, for example a missing `query` or an unrecognized `source_type`.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller lacks access to Brain (Pro or Enterprise plan required) or to this endpoint.
        '402':
          description: Credit limit exceeded.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
  /brain/sources:
    get:
      summary: List sources
      description: |
        List the [Company Brain](/core-concepts/brain) sources the authenticated user can see: personal sources, plus team and organization sources shared with them. Every source type is listed, including ones connected in the app such as Notion or Google Drive.
      operationId: listBrainSources
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources?scope=personal&source_type=direct_file_uploads' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            page = client.brain.list_sources(scope="personal", source_type="direct_file_uploads")
            for source in page.sources:
                print(source.id, source.name, source.status)
      parameters:
        - name: scope
          in: query
          required: false
          schema:
            type: string
            enum: [personal, team, organization]
          description: Only sources in this scope.
        - name: source_type
          in: query
          required: false
          schema:
            type: string
          description: Only sources of this type, for example `direct_file_uploads` or `notion`.
        - name: team_id
          in: query
          required: false
          schema:
            type: string
          description: Only team sources belonging to this team.
        - name: page_size
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Maximum number of sources to return.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque cursor from a previous response's `next_cursor`.
      responses:
        '200':
          description: One page of sources.
          content:
            application/json:
              schema:
                type: object
                required: [sources]
                properties:
                  sources:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrainSource'
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor for the next page, or `null` on the last page.
              example:
                sources:
                  - id: "PFqdAMir8PA2Xc6qcszSN9"
                    name: "Engineering docs"
                    source_type: "direct_file_uploads"
                    status: "active"
                    scope: "personal"
                    team_id: null
                    created_by_user_id: "f6ceNxi7XIMzKJvHFajQaBtnv5h2"
                    created_at: "2026-09-25T23:52:11.046883+00:00"
                next_cursor: null
        '400':
          description: Invalid `scope`, `source_type`, or `page_size`.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller lacks access to Brain (Pro or Enterprise plan required).
      security:
        - bearerAuth: []
    post:
      summary: Create source
      description: |
        Create a file-upload source. Only `direct_file_uploads` sources can be created through the API; connected sources such as Notion or Google Drive are set up in the Gumloop app because they need an account connection.

        By default the source is `active` and indexes (and bills) each file as soon as it is uploaded. Set `require_approval` to `true` to create it as a `draft` instead: uploads then run a credit estimate, the source owner is notified, and nothing is indexed until [Approve source](/api-reference/brain/approve-source) is called.
      operationId: createBrainSource
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"name": "Engineering docs", "scope": "team", "team_id": "5sjVFsvytdaxEvdqV7mnmn"}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            source = client.brain.create_source("Engineering docs", scope="team", team_id="5sjVFsvytdaxEvdqV7mnmn").source
            print(source.id, source.status)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 200
                  description: Display name of the source.
                  example: "Engineering docs"
                source_type:
                  type: string
                  default: direct_file_uploads
                  description: Only `direct_file_uploads` is accepted.
                scope:
                  type: string
                  enum: [personal, team, organization]
                  default: personal
                  description: "Which Brain the source belongs to. `team` requires `team_id`."
                team_id:
                  type: string
                  description: "The team for `scope: team`. Not accepted with other scopes."
                require_approval:
                  type: boolean
                  default: false
                  description: Create as a draft that estimates credits before anything is indexed.
      responses:
        '201':
          description: Source created.
          content:
            application/json:
              schema:
                type: object
                required: [source]
                properties:
                  source:
                    $ref: '#/components/schemas/BrainSource'
        '400':
          description: Invalid body — `source_type_not_supported`, a `team` scope without `team_id`, or `team_id` with another scope.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage sources in the requested scope, or the team is not in their organization.
      security:
        - bearerAuth: []

  /brain/sources/{source_id}:
    get:
      summary: Retrieve source
      description: Fetch one source the authenticated user can see.
      operationId: getBrainSource
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            print(client.brain.get_source("PFqdAMir8PA2Xc6qcszSN9").source.status)
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      responses:
        '200':
          description: The source.
          content:
            application/json:
              schema:
                type: object
                required: [source]
                properties:
                  source:
                    $ref: '#/components/schemas/BrainSource'
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot view this source.
        '404':
          description: Source not found.
      security:
        - bearerAuth: []
    delete:
      summary: Delete source
      description: Delete a source, every file in it, and everything it contributed to search. This cannot be undone.
      operationId: deleteBrainSource
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            client.brain.delete_source("PFqdAMir8PA2Xc6qcszSN9")
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      responses:
        '200':
          description: Source deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source not found.
      security:
        - bearerAuth: []

  /brain/sources/{source_id}/files:
    get:
      summary: List files
      description: |
        List the files in a file-upload source with their indexing status. Each file carries the `sha256` of its bytes, so a client can compare a local folder against the source and upload only what changed.
      operationId: listBrainFiles
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9/files' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            for f in client.brain.list_files("PFqdAMir8PA2Xc6qcszSN9").files:
                print(f.file_name, f.status, f.sha256)
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
        - name: page_size
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: One page of files.
          content:
            application/json:
              schema:
                type: object
                required: [files]
                properties:
                  files:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrainFile'
                  next_cursor:
                    type: string
                    nullable: true
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot view this source.
        '404':
          description: Source not found.
      security:
        - bearerAuth: []
    post:
      summary: Upload files
      description: |
        Upload up to 25 files as `multipart/form-data` parts named `files`. Accepted types are PDF, Word, PowerPoint, Excel, and text formats (`.txt`, `.md`, `.html`, `.csv`, `.rtf`), each up to 25 MB.

        Indexing starts on its own after the upload: an `active` source indexes and bills immediately, a `draft` source runs a credit estimate instead (see [Retrieve estimate](/api-reference/brain/get-estimate)). Poll [List files](/api-reference/brain/list-files) until each file's `status` is `indexed`.

        Files the upload policy refuses (unsupported type, too large, empty) are returned in `rejected` with a `201`; the request is a `400 no_files_accepted` only when every file was refused.
      operationId: uploadBrainFiles
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9/files' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -F 'files=@handbook.pdf' \
              -F 'files=@release-process.md'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            upload = client.brain.upload_files(
                "PFqdAMir8PA2Xc6qcszSN9",
                {"handbook.pdf": open("handbook.pdf", "rb").read()},
            )
            print([f.status for f in upload.files], upload.rejected)
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [files]
              properties:
                files:
                  type: array
                  maxItems: 25
                  description: One part per file. The part's filename becomes the file's name in the source.
                  items:
                    type: string
                    format: binary
      responses:
        '201':
          description: At least one file was stored.
          content:
            application/json:
              schema:
                type: object
                required: [files, rejected]
                properties:
                  files:
                    type: array
                    description: Files that were stored, in `uploaded` status.
                    items:
                      $ref: '#/components/schemas/BrainFile'
                  rejected:
                    type: array
                    description: Files the upload policy refused.
                    items:
                      type: object
                      properties:
                        file_name:
                          type: string
                        error:
                          type: string
                  sync_run_id:
                    type: string
                    nullable: true
                    description: The indexing (or estimate) run this upload started, when one could be queued.
              example:
                files:
                  - id: "f2bf0108-1650-47cd-a20d-15268a3cc5cc"
                    file_name: "handbook.pdf"
                    mime_type: "application/pdf"
                    size_bytes: 48213
                    sha256: "9bae5b3a3eb1af86840cd141aa644f73d55ec445e056b74fb2ecffc17f15ac9d"
                    status: "uploaded"
                    error: null
                    document_id: null
                    created_at: "2026-09-25T23:52:13.997124+00:00"
                    indexed_at: null
                rejected:
                  - file_name: "notes.exe"
                    error: "unsupported file type \".exe\""
                sync_run_id: "4umrkunyhiBgUhLLBT2toA"
        '400':
          description: No `files` part, more than 25 parts, or `no_files_accepted` (every file was refused; the rejections are in `error.details.rejected`).
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source not found.
        '409':
          description: The source is paused (`source_paused`).
        '413':
          description: The request body exceeds the batch limit.
      security:
        - bearerAuth: []

  /brain/sources/{source_id}/files/{file_id}:
    delete:
      summary: Delete file
      description: Remove a file from the source and from search.
      operationId: deleteBrainFile
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9/files/f2bf0108-1650-47cd-a20d-15268a3cc5cc' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            client.brain.delete_file("PFqdAMir8PA2Xc6qcszSN9", "f2bf0108-1650-47cd-a20d-15268a3cc5cc")
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
        - name: file_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: File deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source or file not found.
      security:
        - bearerAuth: []

  /brain/sources/{source_id}/estimate:
    get:
      summary: Retrieve estimate
      description: |
        The latest credit estimate for a source created with `require_approval`. `estimate` is `null` until the first upload has produced a run; poll until `estimate.status` is `paused_for_approval`, then call [Approve source](/api-reference/brain/approve-source). `estimated_credits` is rounded up to the nearest 5 and is an estimate, not a quote.
      operationId: getBrainSourceEstimate
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/brain/sources/jVustMhHWG43CFQBFMckcn/estimate' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            estimate = client.brain.get_estimate("jVustMhHWG43CFQBFMckcn").estimate
            print(estimate and estimate.estimated_credits)
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      responses:
        '200':
          description: The source status and its latest estimate.
          content:
            application/json:
              schema:
                type: object
                required: [source_id, status]
                properties:
                  source_id:
                    type: string
                  status:
                    type: string
                    enum: [draft, active, paused]
                    description: The source's status.
                  estimate:
                    type: object
                    nullable: true
                    properties:
                      sync_run_id:
                        type: string
                      status:
                        type: string
                        description: Run status, `paused_for_approval` once the estimate is ready.
                      estimated_tokens:
                        type: integer
                      estimated_credits:
                        type: integer
                      document_count:
                        type: integer
              example:
                source_id: "jVustMhHWG43CFQBFMckcn"
                status: "draft"
                estimate:
                  sync_run_id: "EEGmBWjtbRzJdixVeSsetY"
                  status: "paused_for_approval"
                  estimated_tokens: 75
                  estimated_credits: 5
                  document_count: 2
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source not found.
      security:
        - bearerAuth: []

  /brain/sources/{source_id}/approve:
    post:
      summary: Approve source
      description: Approve a `draft` source. It becomes `active`, the paused estimate run resumes as a real indexing run, and credits are charged. Later uploads index without another approval.
      operationId: approveBrainSource
      tags:
        - Brain
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/brain/sources/jVustMhHWG43CFQBFMckcn/approve' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            print(client.brain.approve_source("jVustMhHWG43CFQBFMckcn").source.status)
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      responses:
        '200':
          description: Source approved.
          content:
            application/json:
              schema:
                type: object
                required: [source]
                properties:
                  source:
                    $ref: '#/components/schemas/BrainSource'
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source not found.
        '409':
          description: The source is not a draft (`source_not_draft`).
      security:
        - bearerAuth: []
  /skills:
    get:
      summary: List skills
      description: List skills the caller has access to. Filter by team, search by name, or narrow to a specific creator, related MCP server, or agent.
      operationId: listSkills
      tags:
        - Skills
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/skills?team_id=YOUR_TEAM_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.skills.list(team_id="YOUR_TEAM_ID")
            for skill in response.skills:
                print(skill.id, skill.name)
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: Scope the listing to a single team. When omitted, returns skills owned by the authenticated user.
        - in: query
          name: search_query
          required: false
          schema:
            type: string
          description: Case-insensitive substring match against the skill name.
        - in: query
          name: sort_order
          required: false
          schema:
            type: string
            enum: [newest, popular, most_used]
            default: newest
          description: Sort order for the returned skills.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of skills per page. Clamped between 1 and 100.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque pagination cursor returned in `next_cursor` from a prior page.
        - in: query
          name: creator_user_id
          required: false
          schema:
            type: string
          description: Filter to skills created by this user ID.
        - in: query
          name: related_server_id
          required: false
          schema:
            type: string
          description: Filter to skills that reference this MCP server ID in their metadata.
        - in: query
          name: agent_id
          required: false
          schema:
            type: string
          description: Filter to skills attached to this agent.
        - in: query
          name: unused
          required: false
          schema:
            type: string
          description: When set, filters to skills that have not been used.
      responses:
        '200':
          description: Skills matching the provided filters.
          content:
            application/json:
              schema:
                type: object
                properties:
                  skills:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Unique skill identifier.
                          example: "skill_x7y8z9"
                        name:
                          type: string
                          example: "Lead enrichment"
                        description:
                          type: string
                          example: "Enriches inbound leads with firmographics."
                        team_id:
                          type: string
                          description: ID of the team that owns the skill.
                          example: "team_4f8c92ab"
                        created_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: ISO 8601 timestamp of when the skill was created.
                          example: "2026-05-15T14:32:00Z"
                        updated_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: ISO 8601 timestamp of when the skill was last updated.
                          example: "2026-05-16T09:10:00Z"
                        metadata:
                          type: object
                          description: User-defined metadata. May include `related_server_ids` (array of MCP server IDs the skill references).
                          default: {}
                        usage_count:
                          type: integer
                          nullable: true
                          example: 42
                        view_count:
                          type: integer
                          nullable: true
                          example: 117
                        last_used_at:
                          type: string
                          format: date-time
                          nullable: true
                          example: "2026-05-18T22:01:00Z"
                        version_id:
                          type: string
                          nullable: true
                          description: ID of the version exposed by this payload (the current draft, by default).
                          example: "sv_a1b2c3d4"
                        major_version:
                          type: integer
                          nullable: true
                          example: 3
                        is_deployed:
                          type: boolean
                          nullable: true
                          example: true
                        version_created_at:
                          type: string
                          format: date-time
                          nullable: true
                          example: "2026-05-16T09:10:00Z"
                        creator:
                          type: object
                          nullable: true
                          description: Creator of the skill. `null` when no creator is recorded.
                          properties:
                            id:
                              type: string
                              nullable: true
                            first_name:
                              type: string
                              nullable: true
                            last_name:
                              type: string
                              nullable: true
                            email:
                              type: string
                              nullable: true
                            profile_picture:
                              type: string
                              nullable: true
                  next_cursor:
                    type: string
                    nullable: true
                    description: Opaque cursor for the next page, or `null` when there are no more results.
                  total_count:
                    type: integer
                    nullable: true
                    description: Total number of skills matching the filters across all pages.
              examples:
                multiple:
                  summary: Multiple skills
                  value:
                    skills:
                      - id: "skill_x7y8z9"
                        name: "Lead enrichment"
                        description: "Enriches inbound leads with firmographics."
                        team_id: "team_4f8c92ab"
                        created_at: "2026-05-15T14:32:00Z"
                        updated_at: "2026-05-16T09:10:00Z"
                        metadata:
                          related_server_ids:
                            - "apollo"
                        usage_count: 42
                        view_count: 117
                        last_used_at: "2026-05-18T22:01:00Z"
                        version_id: "sv_a1b2c3d4"
                        major_version: 3
                        is_deployed: true
                        version_created_at: "2026-05-16T09:10:00Z"
                        creator:
                          id: "user_2b9d71f0"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                      - id: "skill_p4q5r6"
                        name: "Ticket triage"
                        description: "Classifies and routes support tickets."
                        team_id: "team_4f8c92ab"
                        created_at: "2026-04-02T09:11:00Z"
                        updated_at: "2026-04-02T09:11:00Z"
                        metadata: {}
                        usage_count: 0
                        view_count: 5
                        last_used_at: null
                        version_id: "sv_e5f6g7h8"
                        major_version: 1
                        is_deployed: false
                        version_created_at: "2026-04-02T09:11:00Z"
                        creator:
                          id: "user_2b9d71f0"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                    next_cursor: null
                    total_count: 2
                empty:
                  summary: No matches
                  value:
                    skills: []
                    next_cursor: null
                    total_count: 0
        '400':
          description: Bad request — invalid query parameter (for example, non-integer `page_size`).
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have skill access on the requested team.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
    post:
      summary: Create skill
      description: Upload a skill package and create a new skill. The package must include a `SKILL.md` with `name` and `description` frontmatter; uploads may be a single `.md` file (stored as `SKILL.md`), or a `.zip` / `.skill` archive containing `SKILL.md` at its root. The initial version is created automatically. Maximum upload size is 10 MB.
      operationId: createSkill
      tags:
        - Skills
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/skills' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -F 'team_id=YOUR_TEAM_ID' \
              -F 'files=@./lead-enrichment.skill;type=application/zip'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            with open("lead-enrichment.skill", "rb") as fh:
                response = client.skills.create(
                    files=[("lead-enrichment.skill", fh.read(), "application/zip")],
                    team_id="YOUR_TEAM_ID",
                )
            print(response.skill.id)
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  description: One or more file parts. Accepts a single `.md` file (stored as `SKILL.md`), a `.zip` / `.skill` archive containing `SKILL.md` at its root, or loose files that together include a `SKILL.md`. Total upload must not exceed 10 MB.
                  items:
                    type: string
                    format: binary
                team_id:
                  type: string
                  description: Team that should own the skill. When omitted, the skill is owned by the authenticated user.
              required:
                - files
      responses:
        '201':
          description: Skill created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  skill:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Unique skill identifier.
                        example: "skill_x7y8z9"
                      name:
                        type: string
                        example: "Lead enrichment"
                      description:
                        type: string
                        example: "Enriches inbound leads with firmographics."
                      team_id:
                        type: string
                        description: ID of the team that owns the skill.
                        example: "team_4f8c92ab"
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        description: ISO 8601 timestamp of when the skill was created.
                        example: "2026-05-19T09:00:00Z"
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                        description: ISO 8601 timestamp of when the skill was last updated.
                        example: "2026-05-19T09:00:00Z"
                      metadata:
                        type: object
                        description: User-defined metadata. May include `related_server_ids` (array of MCP server IDs the skill references).
                        default: {}
                      usage_count:
                        type: integer
                        nullable: true
                        example: 0
                      view_count:
                        type: integer
                        nullable: true
                        example: 0
                      last_used_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: null
                      version_id:
                        type: string
                        nullable: true
                        description: ID of the version exposed by this payload (the current draft, by default).
                        example: "sv_a1b2c3d4"
                      major_version:
                        type: integer
                        nullable: true
                        example: 1
                      is_deployed:
                        type: boolean
                        nullable: true
                        example: false
                      version_created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-19T09:00:00Z"
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the skill. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
              examples:
                created:
                  summary: Newly created skill
                  value:
                    skill:
                      id: "skill_x7y8z9"
                      name: "Lead enrichment"
                      description: "Enriches inbound leads with firmographics."
                      team_id: "team_4f8c92ab"
                      created_at: "2026-05-19T09:00:00Z"
                      updated_at: "2026-05-19T09:00:00Z"
                      metadata: {}
                      usage_count: 0
                      view_count: 0
                      last_used_at: null
                      version_id: "sv_a1b2c3d4"
                      major_version: 1
                      is_deployed: false
                      version_created_at: "2026-05-19T09:00:00Z"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
        '400':
          description: Bad request — missing or invalid `SKILL.md`, unsafe archive, or malformed metadata.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot create skills on the requested team.
        '413':
          description: Upload too large — request body exceeded 10 MB.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /skills/{skill_id}:
    patch:
      summary: Update skill
      description: Replace a skill's files with a new upload. Reparses `SKILL.md` to update the skill's `name`, `description`, and `metadata`, and creates a new version. Maximum upload size is 10 MB.
      operationId: updateSkill
      tags:
        - Skills
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/skills/skill_x7y8z9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -F 'files=@./lead-enrichment.skill;type=application/zip'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            with open("lead-enrichment.skill", "rb") as fh:
                response = client.skills.update(
                    "skill_x7y8z9",
                    files=[("lead-enrichment.skill", fh.read(), "application/zip")],
                )
            print(response.skill.version_id)
      parameters:
        - in: path
          name: skill_id
          required: true
          schema:
            type: string
          description: ID of the skill to update.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  description: One or more file parts containing the new skill contents. Same shape as the create endpoint — must include a `SKILL.md`. Total upload must not exceed 10 MB.
                  items:
                    type: string
                    format: binary
              required:
                - files
      responses:
        '200':
          description: Skill updated. The returned `version_id` and `major_version` reflect the new version that was created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  skill:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Unique skill identifier.
                        example: "skill_x7y8z9"
                      name:
                        type: string
                        example: "Lead enrichment"
                      description:
                        type: string
                        example: "Enriches inbound leads with firmographics and intent signals."
                      team_id:
                        type: string
                        description: ID of the team that owns the skill.
                        example: "team_4f8c92ab"
                      created_at:
                        type: string
                        format: date-time
                        nullable: true
                        description: ISO 8601 timestamp of when the skill was created.
                        example: "2026-05-15T14:32:00Z"
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                        description: ISO 8601 timestamp of when the skill was last updated.
                        example: "2026-05-19T11:42:00Z"
                      metadata:
                        type: object
                        description: User-defined metadata. May include `related_server_ids` (array of MCP server IDs the skill references).
                        default: {}
                      usage_count:
                        type: integer
                        nullable: true
                        example: 42
                      view_count:
                        type: integer
                        nullable: true
                        example: 117
                      last_used_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-18T22:01:00Z"
                      version_id:
                        type: string
                        nullable: true
                        description: ID of the version exposed by this payload (the newly created version).
                        example: "sv_b9c8d7e6"
                      major_version:
                        type: integer
                        nullable: true
                        example: 4
                      is_deployed:
                        type: boolean
                        nullable: true
                        example: false
                      version_created_at:
                        type: string
                        format: date-time
                        nullable: true
                        example: "2026-05-19T11:42:00Z"
                      creator:
                        type: object
                        nullable: true
                        description: Creator of the skill. `null` when no creator is recorded.
                        properties:
                          id:
                            type: string
                            nullable: true
                          first_name:
                            type: string
                            nullable: true
                          last_name:
                            type: string
                            nullable: true
                          email:
                            type: string
                            nullable: true
                          profile_picture:
                            type: string
                            nullable: true
              examples:
                updated:
                  summary: Skill after update
                  value:
                    skill:
                      id: "skill_x7y8z9"
                      name: "Lead enrichment"
                      description: "Enriches inbound leads with firmographics and intent signals."
                      team_id: "team_4f8c92ab"
                      created_at: "2026-05-15T14:32:00Z"
                      updated_at: "2026-05-19T11:42:00Z"
                      metadata:
                        related_server_ids:
                          - "apollo"
                      usage_count: 42
                      view_count: 117
                      last_used_at: "2026-05-18T22:01:00Z"
                      version_id: "sv_b9c8d7e6"
                      major_version: 4
                      is_deployed: false
                      version_created_at: "2026-05-19T11:42:00Z"
                      creator:
                        id: "user_2b9d71f0"
                        first_name: "Ada"
                        last_name: "Lovelace"
                        email: "ada@example.com"
                        profile_picture: null
        '400':
          description: Bad request — missing or invalid `SKILL.md`, unsafe archive, or malformed metadata.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot update this skill.
        '404':
          description: Skill not found.
        '413':
          description: Upload too large — request body exceeded 10 MB.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

    delete:
      summary: Delete skill
      description: Permanently delete a skill. This is a soft-delete — the skill will no longer appear in listings or be usable by agents.
      operationId: deleteSkill
      tags:
        - Skills
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/skills/skill_x7y8z9' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.skills.delete("skill_x7y8z9")
            print(response.deleted)
      parameters:
        - in: path
          name: skill_id
          required: true
          schema:
            type: string
          description: ID of the skill to delete.
      responses:
        '200':
          description: Skill deleted successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    description: Whether the skill was successfully deleted.
                    example: true
              examples:
                deleted:
                  summary: Skill deleted
                  value:
                    deleted: true
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot delete this skill.
        '404':
          description: Skill not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /skills/{skill_id}/download:
    get:
      summary: Download skill
      description: Generate a signed URL to download a skill's contents as a `.skill` archive (ZIP). When `version_id` is provided, returns that exact version; otherwise returns the current draft.
      operationId: downloadSkill
      tags:
        - Skills
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/skills/skill_x7y8z9/download' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.skills.download("skill_x7y8z9")
            print(response.download_url)
      parameters:
        - in: path
          name: skill_id
          required: true
          schema:
            type: string
          description: ID of the skill to download.
        - in: query
          name: version_id
          required: false
          schema:
            type: string
          description: Specific version to download. When omitted, the current draft is returned.
      responses:
        '200':
          description: Signed download URL for the requested skill archive.
          content:
            application/json:
              schema:
                type: object
                properties:
                  download_url:
                    type: string
                    description: Short-lived signed URL pointing at the `.skill` archive in object storage.
                  filename:
                    type: string
                    description: Suggested filename for the archive (`<skill name>.skill`).
                  media_type:
                    type: string
                    enum: ["application/zip"]
                    description: Media type of the archive at `download_url`.
                  size:
                    type: integer
                    nullable: true
                    description: Size of the archive in bytes.
                  id:
                    type: string
                    description: ID of the skill the archive was generated for.
                  version_id:
                    type: string
                    nullable: true
                    description: Version that was archived, or `null` if the skill has no current version.
                  major_version:
                    type: integer
                    nullable: true
                    description: Major version number of the archived version, or `null` if unavailable.
              examples:
                download:
                  summary: Signed download URL
                  value:
                    download_url: "https://storage.googleapis.com/gumloop-skills/skill_x7y8z9/Lead%20enrichment.skill?X-Goog-Signature=..."
                    filename: "Lead enrichment.skill"
                    media_type: "application/zip"
                    size: 18432
                    id: "skill_x7y8z9"
                    version_id: "sv_a1b2c3d4"
                    major_version: 3
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller cannot read this skill.
        '404':
          description: Skill, version, or skill files not found.
        '502':
          description: Failed to generate a signed download URL from object storage.
      security:
        - bearerAuth: []
  /agents/{agent_id}/artifacts:
    get:
      summary: List artifacts
      description: List artifacts (files) produced by an agent. Optionally scope to a specific session, search by filename, sort, and paginate. Deleted files are excluded from the results.
      operationId: listArtifacts
      tags:
        - Artifacts
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/AGENT_ID/artifacts?page_size=20' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.artifacts.list(agent_id="AGENT_ID")
            for artifact in response.artifacts:
                print(artifact.id, artifact.filename)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent whose artifacts to list. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: session_id
          required: false
          schema:
            type: string
          description: Filter to artifacts produced within a specific session.
        - in: query
          name: search_query
          required: false
          schema:
            type: string
          description: Case-insensitive substring match against the artifact filename.
        - in: query
          name: sort_order
          required: false
          schema:
            type: string
            default: newest
          description: Sort order for results. Defaults to `newest`.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of artifacts to return per page. Clamped to 1–100.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque pagination cursor returned by a prior call as `next_cursor`.
      responses:
        '200':
          description: Artifacts matching the provided filters.
          content:
            application/json:
              schema:
                type: object
                properties:
                  artifacts:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Unique artifact identifier.
                          example: "art_aBcDeF123"
                        version_id:
                          type: string
                          nullable: true
                          description: ID of this specific artifact version.
                          example: "ver_xYz9012"
                        major_version:
                          type: integer
                          nullable: true
                          example: 2
                        agent_id:
                          type: string
                          nullable: true
                          description: ID of the agent that produced the artifact.
                          example: "abc123DEFghiJKL"
                        session_id:
                          type: string
                          nullable: true
                          description: ID of the session in which the artifact was produced.
                          example: "ses_8h2k4m1n"
                        filename:
                          type: string
                          nullable: true
                          example: "q4_sales_report.pdf"
                        created_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: ISO 8601 timestamp of when the artifact version was created.
                          example: "2026-05-15T14:32:00Z"
                        metadata:
                          type: object
                          description: Arbitrary metadata stored with the artifact version.
                        url:
                          type: string
                          nullable: true
                          description: Signed URL for direct access to the artifact file.
                          example: "https://storage.googleapis.com/..."
                        creator:
                          type: object
                          nullable: true
                          description: The user who created this artifact version. `null` when unknown.
                          properties:
                            id:
                              type: string
                              nullable: true
                              example: "user_19a3bc"
                            first_name:
                              type: string
                              nullable: true
                              example: "Ada"
                            last_name:
                              type: string
                              nullable: true
                              example: "Lovelace"
                            email:
                              type: string
                              nullable: true
                              example: "ada@example.com"
                            profile_picture:
                              type: string
                              nullable: true
                              example: "https://example.com/avatars/ada.png"
                  next_cursor:
                    type: string
                    nullable: true
                    description: Cursor to pass as `cursor` on the next request. `null` when there are no more results.
              examples:
                multiple:
                  summary: Multiple artifacts
                  value:
                    artifacts:
                      - id: "art_aBcDeF123"
                        version_id: "ver_xYz9012"
                        major_version: 2
                        agent_id: "abc123DEFghiJKL"
                        session_id: "ses_8h2k4m1n"
                        filename: "q4_sales_report.pdf"
                        created_at: "2026-05-15T14:32:00Z"
                        metadata:
                          media_type: "application/pdf"
                          size: 12345
                        url: "https://storage.googleapis.com/gumloop-artifacts/art_aBcDeF123?X-Goog-Signature=..."
                        creator:
                          id: "user_19a3bc"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: "https://example.com/avatars/ada.png"
                      - id: "art_gHiJkL456"
                        version_id: "ver_aBc4567"
                        major_version: 1
                        agent_id: "abc123DEFghiJKL"
                        session_id: "ses_8h2k4m1n"
                        filename: "summary.txt"
                        created_at: "2026-05-15T14:30:11Z"
                        metadata: {}
                        url: "https://storage.googleapis.com/gumloop-artifacts/art_gHiJkL456?X-Goog-Signature=..."
                        creator:
                          id: "user_19a3bc"
                          first_name: "Ada"
                          last_name: "Lovelace"
                          email: "ada@example.com"
                          profile_picture: null
                    next_cursor: "eyJjcmVhdGVkX3RzIjoiMjAyNi0wNS0xNVQxNDozMDoxMVoifQ=="
                empty:
                  summary: No artifacts
                  value:
                    artifacts: []
                    next_cursor: null
        '400':
          description: Bad request — `page_size` is not an integer.
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the agent.
        '404':
          description: Agent not found.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /artifacts/{artifact_id}/download:
    get:
      summary: Download artifact
      description: Returns a signed download URL for an artifact, plus its filename, media type, and size. Follow `download_url` to fetch the file bytes.
      operationId: downloadArtifact
      tags:
        - Artifacts
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/artifacts/ARTIFACT_ID/download' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.artifacts.download(artifact_id="ARTIFACT_ID")
            print(response.download_url, response.filename, response.size)
      parameters:
        - in: path
          name: artifact_id
          required: true
          schema:
            type: string
          description: ID of the artifact to download.
        - in: query
          name: version_id
          required: false
          schema:
            type: string
          description: Specific version of the artifact to download. Defaults to the latest version when omitted.
      responses:
        '200':
          description: Signed download URL and file metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  download_url:
                    type: string
                    description: Signed URL the caller can `GET` to fetch the file bytes.
                    example: "https://storage.googleapis.com/gumloop-artifacts/art_aBcDeF123?X-Goog-Signature=..."
                  filename:
                    type: string
                    nullable: true
                    example: "q4_sales_report.pdf"
                  media_type:
                    type: string
                    nullable: true
                    example: "application/pdf"
                  size:
                    type: integer
                    nullable: true
                    description: File size in bytes.
                    example: 12345
                required:
                  - download_url
              examples:
                pdf:
                  summary: PDF artifact
                  value:
                    download_url: "https://storage.googleapis.com/gumloop-artifacts/art_aBcDeF123?X-Goog-Signature=..."
                    filename: "q4_sales_report.pdf"
                    media_type: "application/pdf"
                    size: 12345
        '401':
          description: Unauthorized — missing or invalid API key.
        '403':
          description: Forbidden — the caller does not have read access on the artifact.
        '404':
          description: Artifact not found, version not found, or the underlying file is unavailable.
        '502':
          description: Failed to generate a download URL for the underlying file.
      security:
        - bearerAuth: []

  /browser-profiles:
    get:
      summary: List browser profiles
      description: |
        List the browser profiles you own, or a team's profiles with `team_id`. A browser profile holds the sign-ins an agent's Browser ability uses. Cookie values are never returned; each profile lists its sites and cookie counts.
      operationId: listBrowserProfiles
      tags:
        - Browser profiles
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/browser-profiles' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
            for profile in client.browser_profiles.list().profiles:
                print(profile.profile_id, profile.name, [s.domain for s in profile.sites])
      parameters:
        - in: query
          name: team_id
          required: false
          schema:
            type: string
          description: List a team's profiles instead of your personal ones.
      responses:
        '200':
          description: The profiles.
          content:
            application/json:
              schema:
                type: object
                required: [profiles]
                properties:
                  profiles:
                    type: array
                    items:
                      $ref: '#/components/schemas/BrowserProfile'
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller is not a member of `team_id`.
      security:
        - bearerAuth: []

  /browser-profiles/{profile_id}/cookies:
    post:
      summary: Import cookies into a browser profile
      description: |
        Add sign-in cookies to a browser profile. This is what `gumloop browser import-logins` calls. Send the cookies in Chrome extension (`chrome.cookies.Cookie`) or Chrome DevTools Protocol `Cookie` shape. With `url`, only that site's cookies are kept and the import replaces that site; without it, every site in the payload is imported. Cookies are encrypted with the profile's key before storage and are never returned by any endpoint.

        Use `default` as the `profile_id` to import into the owner's default profile, creating it if needed.
      operationId: importBrowserProfileCookies
      tags:
        - Browser profiles
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/browser-profiles/default/cookies' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"url": "https://mail.google.com", "cookies": [{"name": "SID", "value": "...", "domain": ".google.com", "path": "/", "secure": true, "httpOnly": true, "expirationDate": 1790000000}]}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            result = client.browser_profiles.import_cookies("default", url="https://mail.google.com", cookies=cookies)
            print(result.imported.site, result.imported.cookie_count)
      parameters:
        - in: path
          name: profile_id
          required: true
          schema:
            type: string
          description: A profile id, or `default`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [cookies]
              properties:
                url:
                  type: string
                  maxLength: 2048
                  description: Import only the cookies for this site and replace what the profile had for it. Omit to import every site in `cookies`.
                cookies:
                  type: array
                  minItems: 1
                  description: Cookies in `chrome.cookies.Cookie` or CDP `Cookie` shape.
                  items:
                    type: object
                    additionalProperties: true
                team_id:
                  type: string
                  description: Import into a team-owned profile instead of a personal one.
      responses:
        '200':
          description: Cookies imported.
          content:
            application/json:
              schema:
                type: object
                required: [profile, imported]
                properties:
                  profile:
                    $ref: '#/components/schemas/BrowserProfile'
                  imported:
                    type: object
                    properties:
                      site:
                        type: string
                        nullable: true
                        description: The site imported when `url` was given.
                      cookie_count:
                        type: integer
                      skipped:
                        type: integer
                        description: Cookies dropped because they were expired, malformed, or outside `url`'s site.
                      sites:
                        type: array
                        items:
                          type: string
        '400':
          description: Invalid body, or no usable cookies.
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage variables in `team_id`.
        '404':
          description: Profile not found.
      security:
        - bearerAuth: []

  /teams:
    get:
      summary: List teams
      description: List teams the authenticated caller belongs to.
      operationId: listTeams
      tags:
        - Teams
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/teams' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.teams.list()
            for team in response.teams:
                print(team.id, team.name)
      responses:
        '200':
          description: Teams the caller belongs to.
          content:
            application/json:
              schema:
                type: object
                properties:
                  teams:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Unique team identifier.
                          example: "team_4f8c92ab"
                        name:
                          type: string
                          example: "Acme Sales"
                      required:
                        - id
                        - name
              examples:
                multiple:
                  summary: Multiple teams
                  value:
                    teams:
                      - id: "team_4f8c92ab"
                        name: "Acme Sales"
                      - id: "team_91ab73cd"
                        name: "Acme Support"
        '401':
          description: Unauthorized — missing or invalid API key.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []

  /agents/{agent_id}/evaluations:
    get:
      summary: List evaluations
      description: |
        Returns a cursor-paginated list of evaluation results for a specific agent, newest first.
        Only completed and failed evaluations are returned unless `status` selects another state.

        Each evaluation includes the grade, criteria pass/fail results, extracted data points, applied tags, and sentiment analysis.
      operationId: listEvaluations
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations?page_size=20' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.get(
                "https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations",
                headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
                params={"page_size": 20}
            )
            data = response.json()
            for evaluation in data["evaluations"]:
                print(evaluation["grade"], evaluation["data_results"])
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent whose evaluations to list. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of evaluations to return per page (1-100).
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `next_cursor` field.
        - in: query
          name: grade
          required: false
          schema:
            type: string
            enum: [pass, needs_review, needs_attention]
          description: Filter evaluations by grade.
        - in: query
          name: status
          required: false
          schema:
            type: string
            enum: [queued, in_progress, completed, failed]
          description: Return evaluations in one lifecycle state instead of the default completed and failed set.
        - in: query
          name: session_id
          required: false
          schema:
            type: string
          description: Only evaluations of this session.
        - in: query
          name: organization_evaluation_id
          required: false
          schema:
            type: string
          description: Return the results one organization evaluation produced for this agent instead of the agent's own evaluation results.
        - in: query
          name: created_after
          required: false
          schema:
            type: string
            format: date-time
          description: Only evaluations created at or after this ISO 8601 timestamp. Timestamps without an offset are read as UTC.
        - in: query
          name: created_before
          required: false
          schema:
            type: string
            format: date-time
          description: Only evaluations created before this ISO 8601 timestamp. Timestamps without an offset are read as UTC.
      responses:
        '200':
          description: Paginated list of evaluation results.
          content:
            application/json:
              schema:
                type: object
                properties:
                  evaluations:
                    type: array
                    items:
                      $ref: '#/components/schemas/EvaluationResult'
                  next_cursor:
                    type: string
                    nullable: true
                    description: Pass this value as the `cursor` query parameter to fetch the next page. Null when there are no more results.
              example:
                evaluations:
                  - evaluation_id: "eval_abc123"
                    interaction_id: "int_xyz789"
                    agent_id: "agent_456"
                    created_ts: "2026-06-15T14:30:00+00:00"
                    status: "completed"
                    grade: "pass"
                    call_successful: "success"
                    sentiment: "positive"
                    summary: "User asked about pricing and the agent provided accurate tier information."
                    criteria_results:
                      - id: "crit_1"
                        name: "Accuracy"
                        type: "other"
                        priority: "needs_attention"
                        result: "success"
                        rationale: "All pricing information matched the current rate card."
                      - id: "crit_2"
                        name: "Stayed on Topic"
                        type: "voice_tone"
                        priority: "needs_review"
                        result: "success"
                        rationale: "Agent focused exclusively on the pricing question."
                    data_results:
                      - id: "dp_1"
                        name: "Customer Intent"
                        data_type: "string"
                        value: "pricing inquiry"
                      - id: "dp_2"
                        name: "Confidence Score"
                        data_type: "number"
                        value: 8.5
                    applied_tags:
                      - "PRICING_INQUIRY"
                      - "POSITIVE_FEEDBACK"
                next_cursor: "eyJjcmVhdGVkX3RzIjoiMjAyNi0wNi0xNVQxNDozMDowMCswMDowMCJ9"
        '400':
          description: Invalid grade, status, or timestamp parameter.
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — insufficient permissions on this agent.
      security:
        - bearerAuth: []

  /agents/{agent_id}/evaluations/run:
    post:
      summary: Run evaluations
      description: |
        Grades up to 200 of the agent's finished sessions with its own evaluation configuration. Grading is asynchronous: each accepted session gets a result with `status: queued`; poll it with `GET /agents/{agent_id}/evaluations/{evaluation_id}` until it is `completed` or `failed`. A new result replaces the previous result for that session.

        Sessions are skipped, not rejected, when they are unfinished, incognito, or not owned by this agent (`ineligible`), or already have a queued or running result (`in_flight`, with the existing `result_id`). The caller is charged one credit per queued session. Set `dry_run: true` to see the cost and skips without queuing anything.

        Requires edit access on the agent and a plan with evaluations enabled.
      operationId: runEvaluations
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations/run' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"session_ids": ["SESSION_ID_1", "SESSION_ID_2"]}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            queued = client.agents.run_evaluations("AGENT_ID", session_ids=["SESSION_ID_1", "SESSION_ID_2"])
            for result in queued.results:
                print(result.session_id, result.id)
            for skipped in queued.skipped:
                print(skipped.session_id, skipped.reason)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent that owns the sessions.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [session_ids]
              properties:
                session_ids:
                  type: array
                  minItems: 1
                  maxItems: 200
                  uniqueItems: true
                  items:
                    type: string
                  description: Sessions to grade. Duplicates are rejected.
                dry_run:
                  type: boolean
                  default: false
                  description: Report cost and skipped sessions without queuing.
            example:
              session_ids: ["int_xyz789", "int_abc123"]
      responses:
        '202':
          description: Sessions queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationRunResponse'
        '200':
          description: "Dry run — nothing was queued. `results` lists the sessions that would run with `status: planned` and no `id`."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationRunResponse'
        '400':
          description: Invalid request, evaluations not configured for this agent (`evaluations_not_configured`), or no eligible sessions (`no_valid_interactions`).
        '401':
          description: Unauthorized — missing or invalid credentials.
        '402':
          description: Not enough credits for the sessions that would run (`insufficient_credits`).
        '403':
          description: Forbidden — insufficient permissions on this agent, or evaluations are not available on your plan.
        '404':
          description: Agent not found.
        '409':
          description: Every eligible session is already queued or running (`evaluation_run_in_flight`).
      security:
        - bearerAuth: []

  /agents/{agent_id}/evaluations/metrics:
    get:
      summary: Get evaluation metrics
      description: |
        Returns aggregated grade and tag counts for an agent's evaluations over a time window.
        Useful for dashboards and reporting on agent quality trends.
      operationId: getEvaluationMetrics
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations/metrics?days=30' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.get(
                "https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations/metrics",
                headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
                params={"days": 30}
            )
            metrics = response.json()
            print(f"Pass rate: {metrics['grades'].get('pass', 0)}")
            print(f"Tags: {metrics['tags']}")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: query
          name: days
          required: false
          schema:
            type: integer
            default: 30
            minimum: 1
            maximum: 365
          description: Number of days to look back (1-365). Defaults to 30.
      responses:
        '200':
          description: Aggregated evaluation metrics.
          content:
            application/json:
              schema:
                type: object
                properties:
                  days:
                    type: integer
                    description: The window that was counted.
                  grades:
                    type: object
                    description: Count of evaluations per grade.
                    properties:
                      pass:
                        type: integer
                      needs_review:
                        type: integer
                      needs_attention:
                        type: integer
                  tags:
                    type: object
                    description: Count of evaluations per applied tag.
                    additionalProperties:
                      type: integer
              example:
                days: 30
                grades:
                  pass: 118
                  needs_review: 19
                  needs_attention: 5
                tags:
                  PRICING_INQUIRY: 34
                  SUPPORT_TICKET: 52
                  ESCALATION_NEEDED: 8
                  POSITIVE_FEEDBACK: 45
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — insufficient permissions on this agent.
      security:
        - bearerAuth: []

  /agents/{agent_id}/evaluations/{evaluation_id}:
    get:
      summary: Retrieve evaluation
      description: Retrieve a single evaluation result by ID. The evaluation must belong to the specified agent.
      operationId: retrieveEvaluation
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations/EVALUATION_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.get(
                "https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluations/EVALUATION_ID",
                headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"}
            )
            evaluation = response.json()["evaluation"]
            print(evaluation["grade"], evaluation["summary"])
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent the evaluation belongs to. Also accepts the reserved aliases `gumball` and `analytics`.
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the evaluation to retrieve.
      responses:
        '200':
          description: The requested evaluation result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  evaluation:
                    $ref: '#/components/schemas/EvaluationResult'
              example:
                evaluation:
                  evaluation_id: "eval_abc123"
                  interaction_id: "int_xyz789"
                  agent_id: "agent_456"
                  created_ts: "2026-06-15T14:30:00+00:00"
                  status: "completed"
                  grade: "needs_review"
                  call_successful: "success"
                  sentiment: "negative"
                  summary: "User was frustrated with response time. Agent eventually resolved the issue."
                  criteria_results:
                    - id: "crit_1"
                      name: "Accuracy"
                      type: "other"
                      priority: "needs_attention"
                      result: "success"
                      rationale: "Information provided was correct."
                    - id: "crit_2"
                      name: "Professional Tone"
                      type: "voice_tone"
                      priority: "needs_review"
                      result: "failure"
                      rationale: "Agent used overly casual language in a formal support context."
                  data_results:
                    - id: "dp_1"
                      name: "Resolution Status"
                      data_type: "string"
                      value: "resolved"
                    - id: "dp_2"
                      name: "Handoff Requested"
                      data_type: "boolean"
                      value: false
                  applied_tags:
                    - "SUPPORT_TICKET"
                    - "TONE_ISSUE"
        '401':
          description: Unauthorized — missing or invalid credentials.
        '404':
          description: Evaluation not found or does not belong to this agent.
      security:
        - bearerAuth: []

  /agents/{agent_id}/evaluation-config:
    get:
      summary: Get evaluation config
      description: Retrieve the current evaluation configuration for an agent, including criteria, tags, data points, and sentiment settings.
      operationId: getEvaluationConfig
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluation-config' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.get(
                "https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluation-config",
                headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"}
            )
            config = response.json()["config"]
            print(f"Enabled: {config['enabled']}, Criteria: {len(config['criteria'])}")
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
      responses:
        '200':
          description: The agent's evaluation configuration.
          content:
            application/json:
              schema:
                type: object
                properties:
                  config:
                    $ref: '#/components/schemas/EvaluationConfig'
              example:
                config:
                  agent_id: "agent_456"
                  enabled: true
                  is_active: true
                  model_name: "anthropic/claude-sonnet-4"
                  frequency: "debounced"
                  language: "auto"
                  include_auto_tags: true
                  interaction_types: []
                  criteria:
                    - id: "crit_1"
                      name: "Accuracy"
                      prompt: "The agent provided factually correct information."
                      type: "other"
                      priority: "needs_attention"
                    - id: "crit_2"
                      name: "Professional Tone"
                      prompt: "The agent maintained a professional tone throughout."
                      type: "voice_tone"
                      priority: "needs_review"
                  tags:
                    - name: "PRICING_INQUIRY"
                      description: "User asked about pricing or billing."
                    - name: "ESCALATION_NEEDED"
                      description: "Issue requires human intervention."
                  data_points:
                    - id: "dp_1"
                      name: "Customer Intent"
                      data_type: "string"
                      description: "Summarize the customer's primary intent in 2-5 words."
                    - id: "dp_2"
                      name: "Confidence Score"
                      data_type: "number"
                      description: "Rate 1-10 how confident the agent appeared."
                  sentiment:
                    enabled: true
                    affects_grade: true
                    description: "Focus on the customer's final message tone."
                  updated_ts: "2026-06-10T09:00:00+00:00"
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — insufficient permissions on this agent.
      security:
        - bearerAuth: []
    patch:
      summary: Update evaluation config
      description: |
        Partially update the evaluation configuration for an agent.
        Omitted fields keep their current value. Provided list fields (criteria, tags, data_points) replace that list entirely.

        Requires Pro tier or above.
      operationId: updateEvaluationConfig
      tags:
        - Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluation-config' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "enabled": true,
                "criteria": [
                  {
                    "name": "Accuracy",
                    "prompt": "The agent provided factually correct information.",
                    "type": "other",
                    "priority": "needs_attention"
                  }
                ],
                "data_points": [
                  {
                    "name": "Customer Intent",
                    "data_type": "string",
                    "description": "Summarize the customer primary intent in 2-5 words."
                  }
                ]
              }'
        - lang: python
          label: Python
          source: |
            import requests

            response = requests.patch(
                "https://api.gumloop.com/api/v1/agents/AGENT_ID/evaluation-config",
                headers={
                    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
                    "Content-Type": "application/json"
                },
                json={
                    "enabled": True,
                    "criteria": [
                        {
                            "name": "Accuracy",
                            "prompt": "The agent provided factually correct information.",
                            "type": "other",
                            "priority": "needs_attention"
                        }
                    ]
                }
            )
            config = response.json()["config"]
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
          description: ID of the agent. Also accepts the reserved aliases `gumball` and `analytics`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                  description: Whether evaluations are enabled for this agent.
                model_name:
                  type: string
                  description: LLM model to use for evaluation.
                include_auto_tags:
                  type: boolean
                  description: Allow the evaluator to suggest tags beyond your predefined vocabulary.
                criteria:
                  type: array
                  description: Quality criteria to check (replaces existing list). Max 30.
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      prompt:
                        type: string
                        description: True/false statement to evaluate.
                      type:
                        type: string
                        enum: [prohibited_action, prohibited_words, voice_tone, other]
                      priority:
                        type: string
                        enum: [needs_review, needs_attention]
                tags:
                  type: array
                  description: Tag vocabulary (replaces existing list). Max 50.
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      description:
                        type: string
                data_points:
                  type: array
                  description: Data points to extract (replaces existing list). Max 40.
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      data_type:
                        type: string
                        enum: [string, boolean, integer, number]
                      description:
                        type: string
                sentiment:
                  type: object
                  properties:
                    enabled:
                      type: boolean
                    affects_grade:
                      type: boolean
                    description:
                      type: string
      responses:
        '200':
          description: Updated evaluation configuration.
          content:
            application/json:
              schema:
                type: object
                properties:
                  config:
                    $ref: '#/components/schemas/EvaluationConfig'
        '400':
          description: Invalid request (e.g. exceeds limits).
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — requires Pro tier or insufficient permissions.
      security:
        - bearerAuth: []


  /organizations:
    get:
      summary: List organizations
      description: Returns the organization the authenticated user belongs to. Use its `id` as `organization_id` on the evaluation endpoints.
      operationId: listOrganizations
      tags:
        - Organization
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/organizations' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            organization = client.organizations.list().organizations[0]
            print(organization.id, organization.name)
      responses:
        '200':
          description: The caller's organizations.
          content:
            application/json:
              schema:
                type: object
                properties:
                  organizations:
                    type: array
                    items:
                      $ref: '#/components/schemas/Organization'
              example:
                organizations:
                  - id: "org_4f8c92ab"
                    name: "Acme"
        '401':
          description: Unauthorized — missing or invalid credentials.
      security:
        - bearerAuth: []

  /evaluation-options:
    get:
      summary: Get evaluation options
      description: Allowed values for evaluation fields and filters — session types, criterion types and priorities, data point types, frequencies, grades, statuses, target types, skip reasons — plus size limits. Use these instead of hardcoding enums.
      operationId: getEvaluationOptions
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluation-options' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            options = client.evaluations.options()
            print(options.grades, options.limits["run_session_ids"])
      responses:
        '200':
          description: Option lists and limits.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
              example:
                scopes: ["organization"]
                target_types: ["organization", "team", "user", "agent"]
                session_types: ["chat", "general_agent", "slack", "slack_custom_app", "teams", "email", "operator", "subagent", "triggered", "api", "self_improvement", "reflect", "brain_source_creation", "personalization"]
                default_session_types: ["chat", "general_agent", "slack", "slack_custom_app", "teams"]
                criterion_types: ["prohibited_action", "prohibited_words", "voice_tone", "other"]
                criterion_priorities: ["needs_review", "needs_attention"]
                data_point_types: ["string", "boolean", "integer", "number"]
                frequencies: ["debounced", "per_turn", "manual"]
                grades: ["pass", "needs_review", "needs_attention"]
                statuses: ["queued", "in_progress", "completed", "failed"]
                skip_reasons: ["ineligible", "in_flight"]
                limits:
                  criteria: 30
                  tags: 50
                  data_points: 40
                  tag_name_max_len: 100
                  tag_description_max_len: 500
                  notification_recipients: 50
                  run_session_ids: 200
                  metrics_days: 365
        '401':
          description: Unauthorized — missing or invalid credentials.
      security:
        - bearerAuth: []

  /evaluations:
    get:
      summary: List evaluations
      description: Cursor-paginated list of an organization's evaluations with their targets, coverage, and result rollups. Requires the `organization:manage_evaluations` permission (Enterprise plan).
      operationId: listOrganizationEvaluations
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluations?organization_id=ORG_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.evaluations.list("ORG_ID")
            for evaluation in response.evaluations:
                print(evaluation.name, evaluation.enabled, evaluation.covered_agent_count)
      parameters:
        - in: query
          name: organization_id
          required: true
          schema:
            type: string
          description: The organization whose evaluations to list.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Items per page (1-100).
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `next_cursor`.
      responses:
        '200':
          description: Paginated evaluations.
          content:
            application/json:
              schema:
                type: object
                properties:
                  evaluations:
                    type: array
                    items:
                      $ref: '#/components/schemas/Evaluation'
                  next_cursor:
                    type: string
                    nullable: true
                    description: Pass as `cursor` to fetch the next page. Null when there are no more results.
        '400':
          description: Missing `organization_id`.
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
      security:
        - bearerAuth: []
    post:
      summary: Create evaluation
      description: |
        Creates an organization evaluation. A new evaluation has no targets, so it cannot start enabled: set targets with `PUT /evaluations/{evaluation_id}/targets`, then enable it with `PATCH /evaluations/{evaluation_id}`.

        Rubric values are validated strictly: an unknown `frequency`, criterion `priority`, `type`, data point `data_type`, or session type, a criterion without `name` and `prompt`, or a duplicate tag name returns `400 invalid_request` with the offending paths in `error.details.fields`.
      operationId: createOrganizationEvaluation
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/evaluations' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{
                "organization_id": "ORG_ID",
                "name": "Support tone",
                "config": {
                  "criteria": [
                    {"name": "Greets the customer", "prompt": "Did the agent greet the customer by name?", "priority": "needs_review"}
                  ]
                }
              }'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            created = client.evaluations.create(
                organization_id="ORG_ID",
                name="Support tone",
                config={
                    "criteria": [
                        {
                            "name": "Greets the customer",
                            "prompt": "Did the agent greet the customer by name?",
                            "priority": "needs_review",
                        }
                    ]
                },
            )
            print(created.evaluation.id)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvaluationCreateRequest'
      responses:
        '201':
          description: The created evaluation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationResponse'
        '400':
          description: Invalid request, invalid rubric value (`invalid_request` with `details.fields`), evaluation limit reached, or `enabled` set without targets (`organization_evaluation_no_targets`).
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '409':
          description: An active evaluation with this name already exists (`organization_evaluation_name_taken`).
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}:
    get:
      summary: Retrieve evaluation
      description: Returns one evaluation with its rubric, targets, current coverage, and result rollup.
      operationId: getOrganizationEvaluation
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            evaluation = client.evaluations.retrieve("EVALUATION_ID").evaluation
            print(evaluation.run_summary["success_rate"])
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
      responses:
        '200':
          description: The evaluation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationResponse'
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found or deleted (`organization_evaluation_not_found`).
      security:
        - bearerAuth: []
    patch:
      summary: Update evaluation
      description: |
        Partial update. Only the fields you send change. `config` is merged field by field; a list you send (`criteria`, `tags`, `data_points`) replaces that list wholesale. `description: null` clears the description.

        Setting `enabled: true` requires at least one criterion, tag, or data point (`400 organization_evaluation_empty_rubric`) and at least one covered agent (`400 organization_evaluation_no_targets`). Emptying the rubric of an enabled evaluation pauses it.
      operationId: updateOrganizationEvaluation
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PATCH 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"enabled": true}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.evaluations.update("EVALUATION_ID", enabled=True)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvaluationUpdateRequest'
      responses:
        '200':
          description: The updated evaluation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationResponse'
        '400':
          description: Invalid request, invalid rubric value, or the evaluation cannot be enabled yet.
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found or deleted.
        '409':
          description: An active evaluation with the new name already exists.
      security:
        - bearerAuth: []
    delete:
      summary: Delete evaluation
      description: Deletes the evaluation. It stops running and disappears from lists; results it already produced stay attached to their sessions.
      operationId: deleteOrganizationEvaluation
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X DELETE 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            client.evaluations.delete("EVALUATION_ID")
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
      responses:
        '204':
          description: Deleted.
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found or already deleted.
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}/targets:
    put:
      summary: Set evaluation targets
      description: |
        Replaces the full set of targets — who the evaluation grades. Targets expand to agents live: `organization` covers every agent in the organization, `team` every agent a team owns, `user` a member's personal agents, `agent` one agent. Removing the last target pauses an enabled evaluation; `enabled` in the response reflects that.
      operationId: setOrganizationEvaluationTargets
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X PUT 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID/targets' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"targets": [{"type": "team", "id": "TEAM_ID"}, {"type": "agent", "id": "AGENT_ID"}]}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.evaluations.set_targets(
                "EVALUATION_ID",
                [{"type": "team", "id": "TEAM_ID"}, {"type": "agent", "id": "AGENT_ID"}],
            )
            print(response.covered_agent_count)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [targets]
              properties:
                targets:
                  type: array
                  maxItems: 1000
                  items:
                    $ref: '#/components/schemas/EvaluationTarget'
      responses:
        '200':
          description: The saved targets and resulting coverage.
          content:
            application/json:
              schema:
                type: object
                properties:
                  targets:
                    type: array
                    items:
                      $ref: '#/components/schemas/EvaluationTarget'
                  covered_agent_count:
                    type: integer
                    description: Agents the targets currently expand to.
                  enabled:
                    type: boolean
                    description: Whether the evaluation is still enabled after the change.
              example:
                targets:
                  - type: "team"
                    id: "team_4f8c92ab"
                  - type: "agent"
                    id: "agent_91ab73cd"
                covered_agent_count: 12
                enabled: true
        '400':
          description: Invalid request (for example a `team`, `user`, or `agent` target without an `id`).
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found, or a target is not part of this organization (`organization_evaluation_target_not_in_organization`).
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}/run:
    post:
      summary: Run evaluation on sessions
      description: |
        Grades up to 200 existing sessions with this evaluation. Grading is asynchronous: each accepted session gets a result with `status: queued`; poll it with `GET /evaluations/{evaluation_id}/results/{result_id}` until it is `completed` or `failed`.

        Sessions are skipped, not rejected, when they are not completed sessions of an agent the evaluation covers (`ineligible`) or already have a queued or running result for this evaluation (`in_flight`, with the existing `result_id`). The caller is charged one credit per queued session. Set `dry_run: true` to see the cost and skips without queuing anything.
      operationId: runOrganizationEvaluation
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl -X POST 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID/run' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -H 'Content-Type: application/json' \
              -d '{"session_ids": ["SESSION_ID_1", "SESSION_ID_2"]}'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            queued = client.evaluations.run("EVALUATION_ID", ["SESSION_ID_1", "SESSION_ID_2"])
            for result in queued.results:
                print(result.session_id, result.id)
            for skipped in queued.skipped:
                print(skipped.session_id, skipped.reason)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [session_ids]
              properties:
                session_ids:
                  type: array
                  minItems: 1
                  maxItems: 200
                  uniqueItems: true
                  items:
                    type: string
                  description: Sessions to grade. Duplicates are rejected.
                dry_run:
                  type: boolean
                  default: false
                  description: Report cost and skipped sessions without queuing.
      responses:
        '202':
          description: Sessions queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationRunResponse'
        '200':
          description: "Dry run — nothing was queued. `results` lists the sessions that would run with `status: planned` and no `id`."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationRunResponse'
        '400':
          description: Invalid request, evaluation disabled or empty (`organization_evaluation_not_runnable`), or no session belongs to a covered agent (`organization_evaluation_no_eligible_chats`).
        '401':
          description: Unauthorized — missing or invalid credentials.
        '402':
          description: Not enough credits for the sessions that would run (`insufficient_credits`).
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found.
        '409':
          description: Every eligible session is already queued or running for this evaluation (`organization_evaluation_run_in_flight`).
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}/results:
    get:
      summary: List evaluation results
      description: Cursor-paginated results for one evaluation across every agent it grades, newest first. Each session appears once with its latest result; queued and in-progress results are included so a run can be followed to completion.
      operationId: listOrganizationEvaluationResults
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID/results?grade=needs_attention' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            response = client.evaluations.list_results("EVALUATION_ID", grade="needs_attention")
            for result in response.results:
                print(result.agent_id, result.session_id, result.summary)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
        - in: query
          name: agent_id
          required: false
          schema:
            type: string
          description: Only results for this agent.
        - in: query
          name: session_id
          required: false
          schema:
            type: string
          description: Only results for this session.
        - in: query
          name: grade
          required: false
          schema:
            type: string
            enum: [pass, needs_review, needs_attention]
          description: Filter by grade.
        - in: query
          name: status
          required: false
          schema:
            type: string
            enum: [queued, in_progress, completed, failed]
          description: Filter by status.
        - in: query
          name: created_after
          required: false
          schema:
            type: string
            format: date-time
          description: Only results created at or after this time. RFC 3339 with an explicit offset (for example `2026-09-01T00:00:00Z`).
        - in: query
          name: created_before
          required: false
          schema:
            type: string
            format: date-time
          description: Only results created before this time. RFC 3339 with an explicit offset.
        - in: query
          name: page_size
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Items per page (1-100).
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `next_cursor`.
      responses:
        '200':
          description: Paginated results.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvaluationResultList'
        '400':
          description: Invalid filter value.
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found.
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}/results/{result_id}:
    get:
      summary: Retrieve evaluation result
      description: One result, including per-criterion outcomes, extracted data points, and applied tags. Poll this after `POST /evaluations/{evaluation_id}/run` until `status` is `completed` or `failed`.
      operationId: getOrganizationEvaluationResult
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID/results/RESULT_ID' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            result = client.evaluations.get_result("EVALUATION_ID", "RESULT_ID").result
            print(result.status, result.grade, result.error_code)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
        - in: path
          name: result_id
          required: true
          schema:
            type: string
          description: Result ID from a run response or a results list.
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    $ref: '#/components/schemas/EvaluationResultRecord'
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation or result not found (`result_not_found`).
      security:
        - bearerAuth: []

  /evaluations/{evaluation_id}/metrics:
    get:
      summary: Get evaluation metrics
      description: Grade counts for one evaluation over a trailing window (default 30 days, 1–365).
      operationId: getOrganizationEvaluationMetrics
      tags:
        - Organization Evaluations
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl 'https://api.gumloop.com/api/v1/evaluations/EVALUATION_ID/metrics?days=7' \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            metrics = client.evaluations.metrics("EVALUATION_ID", days=7)
            print(metrics.grades)
      parameters:
        - in: path
          name: evaluation_id
          required: true
          schema:
            type: string
          description: ID of the organization evaluation.
        - in: query
          name: days
          required: false
          schema:
            type: integer
            default: 30
            minimum: 1
            maximum: 365
          description: Window length in days.
      responses:
        '200':
          description: Grade counts in the window.
          content:
            application/json:
              schema:
                type: object
                properties:
                  days:
                    type: integer
                  grades:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Count of completed results per grade. Grades with no results are omitted.
              example:
                days: 7
                grades:
                  pass: 41
                  needs_review: 6
                  needs_attention: 2
        '400':
          description: "`days` outside 1–365."
        '401':
          description: Unauthorized — missing or invalid credentials.
        '403':
          description: Forbidden — not an organization admin, or the organization is not on the Enterprise plan.
        '404':
          description: Evaluation not found.
      security:
        - bearerAuth: []

components:
  schemas:
    Agent:
      type: object
      description: An agent and its whole configuration. Single-agent retrieve, create and update return this
        shape; the list endpoint does not populate the inlined collections, abilities or version. Collection-specific
        writes return their own result shapes.
      properties:
        id:
          type: string
          example: "abc123DEFghiJKL"
        name:
          type: string
          example: "Sales research agent"
        description:
          type: string
          nullable: true
          example: "Researches accounts and drafts outreach"
        team_id:
          type: string
          example: "team_4f8c92ab"
        is_active:
          type: boolean
          example: true
        version:
          type: integer
          nullable: true
          description: Latest configuration version. Send it back as `version` on `PATCH` to refuse the update if the agent changed in between.
          example: 4
        model_name:
          type: string
          nullable: true
          example: "anthropic/claude-sonnet-4"
        system_prompt:
          type: string
          nullable: true
          example: "You are a B2B sales research assistant."
        tools:
          type: array
          description: Connectors, workflows and native abilities the agent can call. Secret references are stripped and custom MCP URLs are redacted. Prefer the `abilities` and `mcp-servers` endpoints over editing this list.
          items:
            $ref: '#/components/schemas/AgentTool'
        metadata:
          $ref: '#/components/schemas/AgentMetadata'
        abilities:
          allOf:
            - $ref: '#/components/schemas/AgentAbilities'
          nullable: true
          description: Native abilities, derived from `tools`. `null` on the list endpoint.
        skill_ids:
          type: array
          nullable: true
          description: IDs of attached skills. `null` when the caller cannot view skills or on the list endpoint; `[]` when none are attached.
          items:
            type: string
          example: ["skill_2b9c", "skill_7f1a"]
        knowledge_sources:
          type: array
          nullable: true
          description: Attached Brain sources and their scope. `null` when the caller cannot view knowledge sources or on the list endpoint.
          items:
            $ref: '#/components/schemas/AgentKnowledgeSource'
        triggers:
          type: array
          nullable: true
          description: Triggers the caller can see. `null` when the caller cannot view triggers or on the list endpoint. Webhook URLs are never included here.
          items:
            $ref: '#/components/schemas/AgentTrigger'
        resources:
          type: array
          items:
            type: object
        folder_id:
          type: string
          nullable: true
          example: "folder_91ab"
        type:
          type: string
          nullable: true
          description: Internal agent type discriminator.
        created_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-05-15T14:32:00Z"
        last_used_at:
          type: string
          format: date-time
          nullable: true
        last_updated_at:
          type: string
          format: date-time
          nullable: true
        active_trigger_count:
          type: integer
          nullable: true
          description: Number of enabled triggers. Only populated on the list endpoint.
        creator:
          type: object
          nullable: true
          properties:
            id:
              type: string
              nullable: true
            first_name:
              type: string
              nullable: true
            last_name:
              type: string
              nullable: true
            email:
              type: string
              nullable: true
            profile_picture:
              type: string
              nullable: true
    AgentTool:
      type: object
      description: One entry of an agent's `tools` list. `type` decides which fields apply. Unknown fields are kept as sent.
      required: [type]
      properties:
        type:
          type: string
          description: '`gumcp_server` and `gumstack_server` are connectors from your catalog; `mcp_server` is a
            custom MCP server keyed by its secret; `saved_item`, `web_search`, `web_fetch`, `image_generator`, `interaction_search`,
            `human_input`, `browser`, `manage_evals`, `manage_wiki` and `sandbox` are native abilities. Any other
            value is a remembered "don''t ask again" decision for a native tool family (for example `trigger_creation`)
            and requires `approval_mode`, with optional `tool_approval_modes`. Unknown entry fields are preserved.'
        server_id:
          type: string
          description: Catalog ID for `gumcp_server` and `gumstack_server`.
          example: "gmail"
        mcp_server_url:
          type: string
          nullable: true
          description: Custom MCP endpoint for mcp_server. URLs are redacted in agent responses.
        secret_id:
          type: string
          nullable: true
          writeOnly: true
          description: Saved custom MCP connection ID for mcp_server. Secret IDs are removed recursively from
            agent responses.
        name:
          type: string
          nullable: true
        credentials_to_use:
          type: object
          description: 'Per-credential-type account selection, e.g. `{"gmail_access_token": {"is_default": true}}`.'
          additionalProperties:
            type: object
            properties:
              secret_id:
                type: string
              is_default:
                type: boolean
              is_personal_credential:
                type: boolean
        credential_mode:
          type: string
          enum: [end_user, agent_owned]
          description: Whose credential the connector runs with. `agent_owned` pins the configured account for everyone who uses the agent.
        approval_mode:
          type: string
          enum: [inherit, "off", all, write, custom]
          description: When the agent must ask before calling this connector. `custom` uses `tool_approval_modes`.
        tool_approval_modes:
          type: object
          additionalProperties:
            type: string
            enum:
            - inherit
            - 'off'
            - all
            - write
            - custom
          description: 'Per-tool approval modes for `approval_mode: custom`.'
        restricted_tools:
          type: array
          items:
            type: string
          description: Tool names the agent may not call on this connector.
        is_incognito:
          type: boolean
        is_disabled:
          type: boolean
          description: On `human_input`, `true` turns Ask Question off.
        saved_item_id:
          type: string
          description: Workflow ID for `saved_item`.
        saved_item_version_id:
          type: string
          nullable: true
          description: Optional version pin for a stored saved_item entry.
        metadata:
          type: object
          description: 'Ability settings. `web_search` / `web_fetch`: `preferred_provider`. `image_generator`: `model`. `browser`: `proxy` (`enabled`, `country`), `profile_id`.'
    AgentMetadata:
      type: object
      description: Agent settings, grouped as the agent panel groups them. On PATCH, objects merge and arrays
        and scalars replace, except model_settings, which replaces the saved object whole. Unknown keys are
        dropped.
      properties:
        icon_url:
          type: string
          nullable: true
        max_steps:
          type: number
          nullable: true
          description: Maximum tool-calling steps per turn. Omit for the platform default.
          example: 30
        model_settings:
          type: object
          description: Advanced model parameters. On PATCH, this object replaces the saved model_settings whole;
            send every field you want to keep. Valid options depend on the model; GET /models lists them.
        suggested_prompts:
          type: array
          items:
            type: object
            required: [title, message]
            properties:
              title:
                type: string
              message:
                type: string
              icon:
                type: string
                nullable: true
              servers:
                type: array
                description: Connector badges for this suggested prompt.
                items:
                  type: object
                  required:
                  - server_id
                  - server_type
                  properties:
                    server_id:
                      type: string
                    server_type:
                      type: string
                default: []
        self_modification:
          type: object
          description: Whether the agent may edit its own instructions.
          properties:
            enabled:
              type: boolean
        app_rules:
          type: object
          nullable: true
          description: Whether the agent may create app rules.
          properties:
            creation_enabled:
              type: boolean
              nullable: true
        app_discovery:
          type: object
          description: Whether the agent may discover and use connectors beyond those attached.
          properties:
            enabled:
              type: boolean
        skills:
          type: object
          description: Whether the agent may create skills.
          properties:
            creation_enabled:
              type: boolean
        triggers:
          type: object
          description: Whether the agent may create triggers.
          properties:
            creation_enabled:
              type: boolean
        tool_discovery:
          type: object
          properties:
            mode:
              type: string
              enum: [auto, enabled]
        tool_approval:
          type: object
          description: Default approval behaviour for connector calls, before per-connector `approval_mode`.
          properties:
            default_mode:
              type: string
              enum: [inherit, "off", all, write, custom]
            reason:
              type: string
            approver_user_id:
              type: string
              nullable: true
        subagent:
          type: object
          properties:
            allow_self_clone:
              type: boolean
            allowed_gummie_ids:
              type: array
              items:
                type: string
              description: Accepted during creation, but ignored by PATCH /agents/{agent_id}. After creation, attach
                and detach subagents with the subagents endpoints.
        artifacts:
          type: object
          description: Default sharing for files the agent creates.
          properties:
            default_access:
              type: string
              enum: [default, organization, anyone]
        voice:
          type: object
          properties:
            enabled:
              type: boolean
            voice:
              type: string
              nullable: true
            speaking_style:
              type: string
              nullable: true
              maxLength: 500
              description: Instructions for how the agent should speak.
        compaction:
          type: object
          description: Chat summarization. Omit to keep the automatic defaults.
          properties:
            override_auto_compaction:
              type: boolean
            summary_model:
              type: string
            context_limit:
              type: integer
            output_reserve_tokens:
              type: integer
            prune_protect_tokens:
              type: integer
            summary_max_tokens:
              type: integer
            proactive_trigger_percent:
              type: integer
        fallback:
          type: object
          description: Model fallback when the primary model is unavailable.
          properties:
            enabled:
              type: boolean
            override_auto_fallback:
              type: boolean
            fallback_models:
              type: array
              items:
                type: string
        image_generation:
          type: object
          properties:
            model:
              type: string
        self_improvement:
          type: object
          description: Reflections. Requires the Reflections tier; the server turns it off otherwise.
          properties:
            enabled:
              type: boolean
            cron_expression:
              type: string
            timezone:
              type: string
        slack:
          type: object
          properties:
            thread_response_trigger:
              type: string
              nullable: true
              enum:
              - on_mention
              - on_any_message
              - auto
            attribution_stamp_enabled:
              type: boolean
            show_detailed_steps:
              type: boolean
        email:
          type: object
          properties:
            attribution_stamp_enabled:
              type: boolean
    AgentAbilities:
      type: object
      description: Native abilities. On `PATCH /agents/{agent_id}/abilities` send only the ones you want to change; each is a small object so settings travel with the toggle.
      properties:
        web_search:
          type: object
          properties:
            enabled:
              type: boolean
            provider:
              type: string
              nullable: true
              description: Preferred search connector, e.g. `exa`. Omit for automatic.
        web_fetch:
          type: object
          properties:
            enabled:
              type: boolean
            provider:
              type: string
              nullable: true
        image_generation:
          type: object
          properties:
            enabled:
              type: boolean
            model:
              type: string
              nullable: true
        search_past_conversations:
          type: object
          properties:
            enabled:
              type: boolean
        ask_question:
          type: object
          description: Whether the agent may pause to ask the person a question.
          properties:
            enabled:
              type: boolean
        tool_discovery:
          type: object
          properties:
            mode:
              type: string
              enum: [auto, enabled]
        manage_evaluations:
          type: object
          properties:
            enabled:
              type: boolean
        browser:
          type: object
          properties:
            enabled:
              type: boolean
            proxy_country:
              type: string
              nullable: true
              description: ISO 3166 alpha-2 country the browser appears to browse from. `null` turns the proxy off.
              example: "us"
            profile_id:
              type: string
              nullable: true
              description: Browser profile whose sign-ins the agent uses. Pinning one switches the browser to agent-owned credentials, as the panel does; `null` clears both.
    ConnectorConfig:
      type: object
      description: Connector settings for `PUT /agents/{agent_id}/mcp-servers/{server_id}`. Identity comes from the path; unknown keys are dropped.
      properties:
        credentials_to_use:
          type: object
          additionalProperties:
            type: object
            properties:
              secret_id:
                type: string
              is_default:
                type: boolean
              is_personal_credential:
                type: boolean
        credential_mode:
          type: string
          enum: [end_user, agent_owned]
        approval_mode:
          type: string
          enum: [inherit, "off", all, write, custom]
        tool_approval_modes:
          type: object
          additionalProperties:
            type: string
            enum: [inherit, "off", all, write]
        restricted_tools:
          type: array
          items:
            type: string
        is_incognito:
          type: boolean
        is_disabled:
          type: boolean
    KnowledgeSourceScope:
      type: object
      nullable: true
      description: Which part of a Brain source the agent reads. `null` is the whole source. Without `mode`, everything except `exclusions`; with `include_only`, only `inclusions`.
      properties:
        mode:
          type: string
          enum: [denylist, include_only]
        inclusions:
          type: array
          items:
            $ref: '#/components/schemas/KnowledgeScopeRule'
        exclusions:
          type: array
          items:
            $ref: '#/components/schemas/KnowledgeScopeRule'
    KnowledgeScopeRule:
      type: object
      required: [type, id]
      properties:
        type:
          type: string
          enum: [container, document]
          description: '`container` is a folder, channel or similar; `document` is one file.'
        id:
          type: string
          description: The item's ID in the source, as returned by `GET /brain/sources/{connector_id}/files`.
        name:
          type: string
          nullable: true
    AgentKnowledgeSource:
      type: object
      properties:
        connector_id:
          type: string
          description: Brain source ID, as returned by `GET /brain/sources`.
          example: "PFqdAMir8PA2Xc6qcszSN9"
        config:
          $ref: '#/components/schemas/KnowledgeSourceScope'
    AgentTrigger:
      type: object
      properties:
        id:
          type: string
          example: "trg_7f1a2b"
        agent_id:
          type: string
          example: "abc123DEFghiJKL"
        type:
          type: string
          description: '`schedule` and `webhook` can be created, fully edited and deleted through the API. Other types (connector events, agent-built triggers) can be renamed, reworded, paused and resumed, but their settings live on the trigger page.'
          example: "schedule"
        name:
          type: string
          nullable: true
        prompt:
          type: string
          nullable: true
          description: The message the agent receives when the trigger fires.
        cron_expression:
          type: string
          nullable: true
          description: Five-field cron, for recurring schedules.
          example: "0 9 * * 1-5"
        run_at:
          type: string
          format: date-time
          nullable: true
          description: One-time run, for `schedule` triggers created with `run_at`.
        timezone:
          type: string
          nullable: true
          example: "America/Los_Angeles"
        pass_raw_data:
          type: boolean
          nullable: true
          description: Webhook only. `true` forwards the request body to the agent instead of `prompt`.
        enabled:
          type: boolean
        status:
          type: string
          nullable: true
        max_failures:
          type: integer
          nullable: true
          description: Consecutive failures before the trigger disables itself.
        webhook_url:
          type: string
          nullable: true
          description: Only on the create response. Fetch it later with `GET /agents/{agent_id}/triggers/{trigger_id}/webhook-url`; it embeds the trigger's secret.
        created_at:
          type: string
          format: date-time
          nullable: true
        last_run_at:
          type: string
          format: date-time
          nullable: true
    AgentAppRule:
      type: object
      description: A connector rule the agent authored for itself. Read only through the API.
      properties:
        id:
          type: string
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        server_id:
          type: string
          nullable: true
        config:
          type: object
        priority:
          type: integer
          nullable: true
    BrainSource:
      type: object
      required: [id, name, source_type, status, scope]
      properties:
        id:
          type: string
          example: "PFqdAMir8PA2Xc6qcszSN9"
        name:
          type: string
          example: "Engineering docs"
        source_type:
          type: string
          description: "`direct_file_uploads` for sources created through the API; connected sources report their type, for example `notion`."
          example: "direct_file_uploads"
        status:
          type: string
          enum: [draft, active, paused]
          description: "`draft` sources only estimate credits until approved."
        scope:
          type: string
          enum: [personal, team, organization]
        team_id:
          type: string
          nullable: true
          description: Set for `team` scope only.
        created_by_user_id:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
    BrowserProfile:
      type: object
      required: [profile_id, owner_id, owner_scope, name]
      properties:
        profile_id:
          type: string
        owner_id:
          type: string
          description: The user or team that owns the profile.
        owner_scope:
          type: string
          enum: [personal, team]
        name:
          type: string
        is_default:
          type: boolean
        version:
          type: integer
        sites:
          type: array
          items:
            type: object
            properties:
              domain:
                type: string
              cookie_count:
                type: integer
              updated_ts:
                type: string
                format: date-time
                nullable: true
        size_bytes:
          type: integer
          nullable: true
        has_storage:
          type: boolean
        created_by_user_id:
          type: string
          nullable: true
        created_ts:
          type: string
          format: date-time
          nullable: true
        updated_ts:
          type: string
          format: date-time
          nullable: true
        last_used_ts:
          type: string
          format: date-time
          nullable: true
    BrainFile:
      type: object
      required: [id, file_name, status]
      properties:
        id:
          type: string
        file_name:
          type: string
        mime_type:
          type: string
        size_bytes:
          type: integer
        sha256:
          type: string
          nullable: true
          description: Hex digest of the uploaded bytes.
        status:
          type: string
          enum: [uploaded, indexing, indexed, failed]
        error:
          type: string
          nullable: true
          description: Why indexing failed, when `status` is `failed`.
        document_id:
          type: string
          nullable: true
          description: Set once the file is searchable; matches `document_id` on search results.
        created_at:
          type: string
          format: date-time
          nullable: true
        indexed_at:
          type: string
          format: date-time
          nullable: true
    Organization:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          nullable: true
    EvaluationTarget:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum: [organization, team, user, agent]
          description: What the target expands to. `user` covers a member's personal agents.
        id:
          type: string
          description: Team, user, or agent ID. Omitted for `organization`; responses return the organization ID.
    EvaluationRubric:
      type: object
      description: What and how the evaluation grades. Entries in `criteria`, `tags`, and `data_points` accept additional fields as the product evolves.
      properties:
        model_name:
          type: string
          description: Grading model. `auto` picks the recommended model.
        frequency:
          type: string
          enum: [debounced, per_turn, manual]
          description: When to grade new sessions automatically. `manual` only grades via `POST /evaluations/{evaluation_id}/run`.
        language:
          type: string
          description: Language for summaries and rationales, or `auto`.
        include_auto_tags:
          type: boolean
        session_types:
          type: array
          items:
            type: string
          description: Session types to grade. See `session_types` in `GET /evaluation-options`.
        criteria:
          type: array
          maxItems: 30
          items:
            type: object
            required: [name, prompt]
            properties:
              id:
                type: string
                description: Assigned by the server when omitted; keep it to update a criterion in place.
              name:
                type: string
              prompt:
                type: string
                description: Plain-language question the grader answers about the session.
              type:
                type: string
                enum: [prohibited_action, prohibited_words, voice_tone, other]
              priority:
                type: string
                enum: [needs_review, needs_attention]
                description: The grade a failing session receives.
              enabled:
                type: boolean
                default: true
        tags:
          type: array
          maxItems: 50
          items:
            type: object
            required: [name]
            properties:
              name:
                type: string
                description: Stored upper-snake-cased (`refund request` becomes `REFUND_REQUEST`).
              description:
                type: string
        data_points:
          type: array
          maxItems: 40
          items:
            type: object
            required: [name]
            properties:
              id:
                type: string
              name:
                type: string
              data_type:
                type: string
                enum: [string, boolean, integer, number]
              description:
                type: string
        sentiment:
          type: object
          additionalProperties: true
        notifications:
          type: object
          additionalProperties: true
    Evaluation:
      type: object
      properties:
        id:
          type: string
        scope:
          type: string
          enum: [organization]
        agent_id:
          type: string
          nullable: true
          description: Reserved for agent-scoped evaluations; null today.
        organization_id:
          type: string
        name:
          type: string
        description:
          type: string
          nullable: true
        enabled:
          type: boolean
          description: Whether new sessions of covered agents are graded and manual runs are allowed.
        targets:
          type: array
          items:
            $ref: '#/components/schemas/EvaluationTarget'
        covered_agent_count:
          type: integer
          description: Agents the targets currently expand to.
        config:
          $ref: '#/components/schemas/EvaluationRubric'
        run_summary:
          type: object
          additionalProperties: true
          description: Rollup of the latest result per graded session — `evaluated_count`, `graded_count`, `pass_count`, `needs_review_count`, `needs_attention_count`, `failed_count`, `success_rate`, `last_run_at`.
        creator:
          type: object
          nullable: true
          properties:
            id:
              type: string
            first_name:
              type: string
              nullable: true
            last_name:
              type: string
              nullable: true
            email:
              type: string
              nullable: true
            profile_picture:
              type: string
              nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      example:
        id: "1c1b3c8e-7d61-4c1e-9c66-4a8d2f1e0b7a"
        scope: "organization"
        agent_id: null
        organization_id: "org_4f8c92ab"
        name: "Support tone"
        description: "Customer-facing agents stay polite and on topic."
        enabled: true
        targets:
          - type: "team"
            id: "team_4f8c92ab"
        covered_agent_count: 12
        config:
          model_name: "auto"
          frequency: "debounced"
          language: "auto"
          include_auto_tags: true
          session_types: ["chat", "slack"]
          criteria:
            - id: "crit_1"
              name: "Greets the customer"
              prompt: "Did the agent greet the customer by name?"
              type: "voice_tone"
              priority: "needs_review"
              enabled: true
          tags: []
          data_points: []
        run_summary:
          evaluated_count: 48
          graded_count: 47
          pass_count: 41
          needs_review_count: 4
          needs_attention_count: 2
          failed_count: 1
          success_rate: 0.872
          last_run_at: "2026-09-03T21:40:00+00:00"
        creator:
          id: "user_1a2b3c"
          first_name: "Ada"
          last_name: "Lovelace"
          email: "ada@acme.com"
          profile_picture: null
        created_at: "2026-09-01T12:00:00+00:00"
        updated_at: "2026-09-03T21:40:00+00:00"
    EvaluationResponse:
      type: object
      properties:
        evaluation:
          $ref: '#/components/schemas/Evaluation'
    EvaluationCreateRequest:
      type: object
      required: [organization_id, name]
      additionalProperties: false
      properties:
        scope:
          type: string
          enum: [organization]
          default: organization
        organization_id:
          type: string
        name:
          type: string
          minLength: 1
          maxLength: 256
          description: Unique among the organization's active evaluations.
        description:
          type: string
          maxLength: 4000
          nullable: true
        enabled:
          type: boolean
          description: Must be omitted or false on create; a new evaluation has no targets yet.
        config:
          $ref: '#/components/schemas/EvaluationRubric'
    EvaluationUpdateRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 256
        description:
          type: string
          maxLength: 4000
          nullable: true
          description: Null clears the description.
        enabled:
          type: boolean
        config:
          $ref: '#/components/schemas/EvaluationRubric'
    EvaluationResultRecord:
      type: object
      properties:
        id:
          type: string
          description: Result ID.
        evaluation_id:
          type: string
          nullable: true
          description: The organization evaluation that produced this result. Null when the agent's own evaluation did.
        session_id:
          type: string
        agent_id:
          type: string
        status:
          type: string
          enum: [queued, in_progress, completed, failed]
        grade:
          type: string
          nullable: true
          enum: [pass, needs_review, needs_attention]
          description: Set once `status` is `completed`.
        error_code:
          type: string
          nullable: true
          description: Set when `status` is `failed`, for example `evaluation_model_error`, `evaluation_dispatch_error`, or `skipped_not_terminal_state`.
        model_name:
          type: string
          nullable: true
        call_successful:
          type: string
          nullable: true
          enum: [success, failure, unknown]
        sentiment:
          type: string
          nullable: true
          enum: [positive, neutral, negative]
        summary:
          type: string
          nullable: true
        criteria_results:
          type: array
          items:
            type: object
            additionalProperties: true
        data_results:
          type: array
          items:
            type: object
            additionalProperties: true
        applied_tags:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
      example:
        id: "9f2d6a41-3b7c-4e0a-8f11-2c5d7e9b0a3f"
        evaluation_id: "1c1b3c8e-7d61-4c1e-9c66-4a8d2f1e0b7a"
        session_id: "int_xyz789"
        agent_id: "agent_456"
        status: "completed"
        grade: "pass"
        error_code: null
        model_name: "auto"
        call_successful: "success"
        sentiment: "positive"
        summary: "Customer asked about a refund window; the agent answered accurately and politely."
        criteria_results:
          - id: "crit_1"
            name: "Greets the customer"
            result: "success"
            rationale: "The agent opened with the customer's name."
        data_results: []
        applied_tags: ["REFUND_REQUEST"]
        created_at: "2026-09-03T21:40:00+00:00"
    EvaluationResultList:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/EvaluationResultRecord'
        next_cursor:
          type: string
          nullable: true
          description: Pass as `cursor` to fetch the next page. Null when there are no more results.
    EvaluationRunResponse:
      type: object
      properties:
        dry_run:
          type: boolean
        credit_cost:
          type: integer
          description: Credits charged (or, on a dry run, that would be charged) — one per queued session.
        results:
          type: array
          description: Sessions accepted, in request order.
          items:
            type: object
            properties:
              id:
                type: string
                nullable: true
                description: Result ID to poll. Null on a dry run.
              session_id:
                type: string
              status:
                type: string
                enum: [queued, planned]
        skipped:
          type: array
          items:
            type: object
            properties:
              session_id:
                type: string
              reason:
                type: string
                enum: [ineligible, in_flight]
                description: "`ineligible`: not a completed session of a covered agent. `in_flight`: already queued or running for this evaluation."
              result_id:
                type: string
                nullable: true
                description: The in-flight result, when `reason` is `in_flight`.
      example:
        dry_run: false
        credit_cost: 2
        results:
          - id: "9f2d6a41-3b7c-4e0a-8f11-2c5d7e9b0a3f"
            session_id: "int_xyz789"
            status: "queued"
          - id: "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9"
            session_id: "int_abc123"
            status: "queued"
        skipped:
          - session_id: "int_old001"
            reason: "ineligible"
            result_id: null
    QueuedMessage:
      type: object
      properties:
        id:
          type: string
          description: Unique ID of the queued message.
          example: "qmsg_1a2b3c"
        input:
          type: string
          nullable: true
          description: The message content.
          example: "Also check their latest funding round."
        state:
          type: string
          enum: [queued, editing]
          description: '`queued` messages are eligible to send; `editing` messages are mid-edit and skipped until the edit finishes.'
          example: "queued"
        position:
          type: integer
          description: 1-based position in the queue.
          example: 1
        created_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-05-15T14:36:00Z"
        updated_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-05-15T14:36:00Z"

    RoleCreditLimit:
      type: object
      properties:
        role_id:
          type: string
          description: The ID of the custom role.
        role_name:
          type: string
          description: The name of the custom role.
        is_default:
          type: boolean
          description: Whether this is a default role that new organization members are assigned to.
        member_count:
          type: integer
          description: Number of users currently assigned to this role.
        monthly_credit_limit:
          type: integer
          nullable: true
          description: The monthly credit limit applied to each member of this role, or null when the role sets no limit.

    EvaluationResult:
      type: object
      properties:
        evaluation_id:
          type: string
          description: Unique ID of this evaluation result. Same value as `id`.
        id:
          type: string
          description: Unique ID of this evaluation result.
        interaction_id:
          type: string
          description: ID of the session that was evaluated. Same value as `session_id`.
        session_id:
          type: string
          description: ID of the session that was evaluated.
        agent_id:
          type: string
          description: ID of the agent.
        created_ts:
          type: string
          format: date-time
          description: When the evaluation was created. Same value as `created_at`.
        created_at:
          type: string
          format: date-time
        status:
          type: string
          enum: [queued, in_progress, completed, failed]
          description: Lifecycle status. Lists return completed and failed evaluations unless the `status` filter selects another state.
        error_code:
          type: string
          nullable: true
          description: Set when `status` is `failed`.
        model_name:
          type: string
          nullable: true
        organization_evaluation_id:
          type: string
          nullable: true
          description: Set when an organization evaluation produced this result; null for the agent's own evaluation.
        grade:
          type: string
          nullable: true
          enum: [pass, needs_review, needs_attention]
          description: Overall grade computed from criteria, sentiment, and call outcome. Null until the evaluation completes.
        call_successful:
          type: string
          enum: [success, failure, unknown]
          description: Whether the agent's call/task was successful.
        sentiment:
          type: string
          nullable: true
          enum: [positive, neutral, negative]
          description: Detected sentiment of the interaction (null if sentiment analysis is disabled).
        summary:
          type: string
          nullable: true
          description: One or two sentence narrative of what happened in the interaction.
        criteria_results:
          type: array
          description: Per-criterion pass/fail results with rationales.
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              type:
                type: string
                enum: [prohibited_action, prohibited_words, voice_tone, other]
              priority:
                type: string
                enum: [needs_review, needs_attention]
              result:
                type: string
                enum: [success, failure, unknown]
              rationale:
                type: string
        data_results:
          type: array
          description: Extracted data point values from the conversation.
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              data_type:
                type: string
                enum: [string, boolean, integer, number]
              value:
                description: The extracted value. Type depends on data_type. Null if the evaluator couldn't find the information.
                nullable: true
        applied_tags:
          type: array
          description: Tags applied to this evaluation.
          items:
            type: string
    EvaluationConfig:
      type: object
      properties:
        agent_id:
          type: string
        enabled:
          type: boolean
        is_active:
          type: boolean
        model_name:
          type: string
          nullable: true
        frequency:
          type: string
          nullable: true
        language:
          type: string
          nullable: true
        include_auto_tags:
          type: boolean
        interaction_types:
          type: array
          items:
            type: string
        criteria:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              prompt:
                type: string
              type:
                type: string
              priority:
                type: string
        tags:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              description:
                type: string
        data_points:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              data_type:
                type: string
              description:
                type: string
        sentiment:
          type: object
          nullable: true
          properties:
            enabled:
              type: boolean
            affects_grade:
              type: boolean
            description:
              type: string
        updated_ts:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
        organization_evaluations:
          type: array
          description: Organization evaluations enforced on this agent (read-only here) — `id`, `name`, `provenance`, `enabled`, and this agent's `run_summary` for each.
          items:
            type: object
            additionalProperties: true
  parameters:
    BrainSourceId:
      name: source_id
      in: path
      required: true
      schema:
        type: string
      description: The source id.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A personal API key or an [OAuth 2.0](/api-reference/oauth) access token. Personal API keys also require the `x-auth-key` header with your user ID.
