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

# Approve, reject, or request changes on a TIL request

> Records the technician's decision (409 if it was already resolved). Returns the updated request. `approve` authorizes the action; `reject` stops it; `request_changes` does neither — it records COMMENTED and unlocks no write. For a request raised by an agent run, that run resumes to revise its proposal and raise a fresh request. For a request raised in a chat with a conversational agent, the agent reads the note on the next message in that chat — the dashboard and the Teams bot send that message; a direct API caller must send one. `request_changes` needs a non-empty `note` and is accepted only on agent requests (400 otherwise). A **platform** service account may pass `client_id` to resolve a TIL in another tenant — but only when the acting technician is a recipient of the request, the request belongs to a conversational chat agent, or it belongs to that tenant's Neo Support agent (any other agent → 403); any non-platform caller passing `client_id` is rejected with 403. For proactive Teams cards the trusted relay may instead supply `acting_end_user_tenant_id` with `acting_end_user`: the backend locates the request's workspace and checks the full identity, named recipient, and current workspace eligibility. Non-platform callers cannot use this identity field.



## OpenAPI

````yaml https://api.neoagent.io/public-api/openapi.json post /public-api/til-requests/{request_id}/decision
openapi: 3.1.0
info:
  description: >-
    Neo's public contract for the dashboard ChatAgent, partner integrations, and
    MSP automation. Every response is wrapped in a `{data, meta}` envelope;
    errors use `{error: {code, message, details?}, meta: {request_id}}`.
    Authenticate with a `Bearer neo_sk_<env>_<secret>` API key (service account)
    or a Microsoft Entra ID JWT (dashboard user). Signed-URL endpoints (end-user
    feedback links) take a `signature` query parameter instead.
  title: Neo Public API
  version: 1.0.0
servers:
  - url: https://api.neoagent.io
security: []
tags:
  - description: Service metadata — health, OpenAPI.
    name: Meta
  - description: Agents and workflows — read, version history, delete, stats.
    name: Agents
  - description: Agent/workflow execution history, sub-resources, retry/cancel.
    name: Executions
  - description: >-
      Every tool call the tenant's agents made in the last 30 days, within the
      caller's workflow access.
    name: Audit Log
  - description: PSA webhook events and their workflow-match results.
    name: Callbacks
  - description: Technician-in-the-loop approval requests.
    name: TIL requests
  - description: RMM script executions triggered by agents.
    name: RMM scripts
  - description: Dispatch-agent field-update decisions.
    name: Dispatch
  - description: The authenticated tenant.
    name: Tenant
  - description: Agent-builder schema catalogs (raw JSON payloads).
    name: Schemas
  - description: Escalate to the Neo team (HubSpot ticket).
    name: Escalation
  - description: Tenant settings.
    name: Settings
  - description: Tenant API-key management (dashboard JWT only).
    name: API keys
  - description: End-user feedback links (signed-URL auth).
    name: Feedback
  - description: End-client companies (CRUD + bulk-update).
    name: End companies
  - description: Channels — bind a CONVERSATIONAL agent to a transport (Teams).
    name: Channels
  - description: PSA/RMM/M365 integration status and connection management.
    name: Integrations
  - description: Technician roster (controls TIL routing and paging).
    name: Technicians
  - description: Future runs queued for TRIGGERED agents.
    name: Scheduled work
  - description: Subscription state and customer-facing credit usage (no provider $).
    name: Billing
  - description: Inbox messages and announcements.
    name: Inbox & Comms
  - description: Tenant-authored agent skills (CRUD) and the built-in skill catalog.
    name: Skills
paths:
  /public-api/til-requests/{request_id}/decision:
    post:
      tags:
        - TIL requests
      summary: Approve, reject, or request changes on a TIL request
      description: >-
        Records the technician's decision (409 if it was already resolved).
        Returns the updated request. `approve` authorizes the action; `reject`
        stops it; `request_changes` does neither — it records COMMENTED and
        unlocks no write. For a request raised by an agent run, that run resumes
        to revise its proposal and raise a fresh request. For a request raised
        in a chat with a conversational agent, the agent reads the note on the
        next message in that chat — the dashboard and the Teams bot send that
        message; a direct API caller must send one. `request_changes` needs a
        non-empty `note` and is accepted only on agent requests (400 otherwise).
        A **platform** service account may pass `client_id` to resolve a TIL in
        another tenant — but only when the acting technician is a recipient of
        the request, the request belongs to a conversational chat agent, or it
        belongs to that tenant's Neo Support agent (any other agent → 403); any
        non-platform caller passing `client_id` is rejected with 403. For
        proactive Teams cards the trusted relay may instead supply
        `acting_end_user_tenant_id` with `acting_end_user`: the backend locates
        the request's workspace and checks the full identity, named recipient,
        and current workspace eligibility. Non-platform callers cannot use this
        identity field.
      operationId: public_api.til_requests_decision_post
      parameters:
        - in: path
          name: request_id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TilDecisionRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    $ref: '#/components/schemas/TilRequest'
                  meta:
                    $ref: '#/components/schemas/SuccessMeta'
                required:
                  - data
                  - meta
                type: object
          description: Success.
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Bad request — malformed input.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Unauthenticated — missing or invalid credentials.
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Forbidden — authenticated but not allowed.
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Not found.
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Conflict — the resource is in a state that blocks this operation.
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Request validation failed.
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Rate limited — see Retry-After.
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
          description: Internal server error.
      security:
        - bearerAuth: []
components:
  schemas:
    TilDecisionRequest:
      properties:
        acting_end_user:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            The technician resolving the request, as their stable user id (M365
            AAD object id). Honored only for a platform service account: it
            authorizes resolving a proactive (Teams) TIL when this technician is
            one of its recipients, or a conversational (chat) MSP agent's TIL,
            and records them as the approver. Ignored for other callers.
          title: Acting End User
        acting_end_user_name:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            Display name of the resolving technician (acting_end_user), for the
            audit record. Honored only for a platform service account; falls
            back to the user id when absent.
          title: Acting End User Name
        acting_end_user_tenant_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            For the trusted Teams relay only: the authenticated sender's
            Microsoft directory. For proactive cards, resolves the workspace
            from the request and verifies the recipient's current access. Must
            come from the Teams activity, never card data.
          title: Acting End User Tenant Id
        action:
          description: >-
            "approve" authorizes the pending action (with a note, the agent must
            honour it). "reject" stops it. "request_changes" does neither: the
            note is required and nothing is authorized. A request raised by an
            agent run resumes that run to revise its proposal and raise a fresh
            request; a request raised in a chat with a conversational agent is
            read on the next message in that chat, which the dashboard and Teams
            send for you and a direct API caller must send. Only agent (AGENTIC)
            requests accept request_changes.
          enum:
            - approve
            - reject
            - request_changes
          title: Action
          type: string
        advance_rotation:
          default: false
          description: >-
            Ticket Dispatch only, and only with action "reject". False (the
            default) means do not auto-assign this ticket: Neo stops dispatching
            it and leaves it for manual assignment, which is what a rejection
            means everywhere else. True declines only the proposed technician
            and lets Neo offer the ticket to the next available one, excluding
            everyone who has already declined it. Ignored for every other
            request type.
          title: Advance Rotation
          type: boolean
        client_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            Target tenant whose TIL request to resolve. Honored only for a
            platform service account; any other caller passing it gets 403. A
            platform caller may resolve a tenant's TIL when the acting
            technician (see acting_end_user) is one of its recipients, or when
            it belongs to that tenant's Neo Support agent. Omit to resolve in
            the caller's own tenant.
          title: Client Id
        note:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Note
      required:
        - action
      title: TilDecisionRequest
      type: object
    TilRequest:
      properties:
        agent_autonomy_type:
          anyOf:
            - $ref: '#/components/schemas/AutonomyType'
            - type: 'null'
        agent_id:
          anyOf:
            - type: integer
            - type: 'null'
          title: Agent Id
        agent_name:
          anyOf:
            - type: string
            - type: 'null'
          title: Agent Name
        approvers:
          items:
            $ref: '#/components/schemas/TilApprover'
          title: Approvers
          type: array
        comment:
          anyOf:
            - type: string
            - type: 'null'
          title: Comment
        consent_status:
          $ref: '#/components/schemas/ConsentStatus'
        created_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          title: Created At
        decided_by:
          anyOf:
            - $ref: '#/components/schemas/User'
            - type: 'null'
        id:
          title: Id
          type: integer
        requested_by:
          anyOf:
            - $ref: '#/components/schemas/User'
            - type: 'null'
        requested_for:
          additionalProperties: true
          title: Requested For
          type: object
        session_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Session Id
        ticket_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Ticket Id
        ticket_number:
          anyOf:
            - type: string
            - type: 'null'
          title: Ticket Number
        topic:
          $ref: '#/components/schemas/Topic'
        updated_at:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          title: Updated At
      required:
        - id
        - agent_id
        - agent_name
        - agent_autonomy_type
        - ticket_id
        - ticket_number
        - topic
        - consent_status
        - requested_for
        - approvers
        - decided_by
        - requested_by
        - comment
        - session_id
        - created_at
        - updated_at
      title: TilRequest
      type: object
    SuccessMeta:
      properties:
        pagination:
          $ref: '#/components/schemas/Pagination'
        request_id:
          format: uuid
          type: string
        timings_ms:
          additionalProperties:
            type: number
          type: object
        warnings:
          description: >-
            Non-fatal warnings about the created/updated resource (e.g. an
            unhealthy PSA callback).
          items:
            type: string
          type: array
      required:
        - request_id
        - timings_ms
      type: object
    ErrorEnvelope:
      properties:
        error:
          properties:
            code:
              description: Stable machine-readable error code.
              type: string
            details:
              additionalProperties: true
              type: object
            message:
              type: string
          required:
            - code
            - message
          type: object
        meta:
          properties:
            request_id:
              format: uuid
              type:
                - string
                - 'null'
          type: object
      required:
        - error
        - meta
      type: object
    AutonomyType:
      enum:
        - CHAINED_ACTIONS
        - AGENTIC
        - CONVERSATIONAL
      title: AutonomyType
      type: string
    TilApprover:
      description: >-
        A technician the approval request was sent to — i.e. who is eligible to
        approve it.
      properties:
        user_id:
          title: User Id
          type: string
        user_name:
          title: User Name
          type: string
      required:
        - user_id
        - user_name
      title: TilApprover
      type: object
    ConsentStatus:
      enum:
        - PENDING
        - APPROVED
        - COMMENTED_AND_APPROVED
        - REJECTED
        - COMMENTED
      title: ConsentStatus
      type: string
    User:
      properties:
        id:
          title: Id
          type: string
        name:
          title: Name
          type: string
      required:
        - id
        - name
      title: User
      type: object
    Topic:
      enum:
        - TICKET
        - CONFIGURATION
        - PROJECT
        - PROJECT_TICKET
        - TEAM_MEMBER
        - CONTACT
        - CLIENT
        - TIME_ENTRY
        - SCHEDULED_ENTRY
        - ONBOARD_USER
        - OFFBOARD_USER
        - DISPATCH
        - GENERAL_QA
        - EXECUTE_RMM_COMPONENT
        - SEND_END_USER_MESSAGE
        - AGENTIC_WORKFLOW
      title: Topic
      type: string
    Pagination:
      properties:
        has_more:
          type: boolean
        next_cursor:
          type:
            - string
            - 'null'
      required:
        - next_cursor
        - has_more
      type: object
  securitySchemes:
    bearerAuth:
      description: >-
        `Authorization: Bearer <token>` where `<token>` is either a
        `neo_sk_<env>_<secret>` API key (service account) or a Microsoft Entra
        ID access token (dashboard user).
      scheme: bearer
      type: http

````