openapi: 3.0.3
info:
  title: SOAT Agent Sessions API
  version: 1.0.0
  description: >
    Agent Sessions provide a simplified 1-user ↔ 1-agent conversational
    abstraction. Sessions are nested under agents and manage the underlying
    conversation, actors, and generation plumbing automatically.
  contact:
    name: SOAT Team
    url: https://github.com/ttoss/soat
servers:
  - url: '{baseUrl}'
    description: Base URL of your SOAT deployment (e.g. https://your-soat.com or http://localhost:5047)
    variables:
      baseUrl:
        description: The base URL of your SOAT deployment
        default: http://localhost:5047
tags:
  - name: Sessions
    description: Manage agent sessions
security:
  - bearerAuth: []
paths:
  /api/v1/agents/{agent_id}/sessions:
    post:
      tags:
        - Sessions
      summary: Create a session
      description: >
        Creates a new session for the specified agent. Internally creates a
        conversation and two actors (agent + user) so the caller only needs
        this single call to start interacting with the agent.
      operationId: createAgentSession
      parameters:
        - $ref: '#/components/parameters/AgentId'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionRequest'
      responses:
        '201':
          description: Session created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    get:
      tags:
        - Sessions
      summary: List sessions
      description: Returns sessions for the specified agent, optionally filtered by actorId and status.
      operationId: listAgentSessions
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - name: actor_id
          in: query
          required: false
          description: Filter by actor public ID
          schema:
            type: string
        - name: status
          in: query
          required: false
          description: Filter by session status (open or closed)
          schema:
            type: string
            enum: [open, closed]
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Paginated list of sessions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SessionRecord'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/agents/{agent_id}/sessions/{session_id}:
    get:
      tags:
        - Sessions
      summary: Get a session
      description: Returns details of a single session.
      operationId: getAgentSession
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Sessions
      summary: Update a session
      description: Updates the session name and/or status.
      operationId: updateSession
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSessionRequest'
      responses:
        '200':
          description: Updated session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Sessions
      summary: Delete a session
      description: Deletes the session and its underlying conversation and actors.
      operationId: deleteAgentSession
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      responses:
        '204':
          description: Session deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/agents/{agent_id}/sessions/{session_id}/messages:
    get:
      tags:
        - Sessions
      summary: List session messages
      description: >
        Returns messages in the session with simplified roles (user/assistant)
        instead of raw actor IDs.
      operationId: listAgentSessionMessages
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Paginated list of messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SessionMessage'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Sessions
      summary: Add a user message
      description: >
        Saves a user message to the session. When autoGenerate is enabled on the
        session and no generation is currently in progress, generation is triggered
        automatically and the response mirrors GenerateSessionResponse. Otherwise
        returns the saved user message.
      operationId: addSessionMessage
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddSessionMessageRequest'
      responses:
        '201':
          description: User message saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddSessionMessageResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/agents/{agent_id}/sessions/{session_id}/generate:
    post:
      tags:
        - Sessions
      summary: Trigger agent generation
      description: >
        Triggers the agent to generate a response based on the current
        conversation. Returns the assistant reply or a requires_action status
        if the agent needs client tool outputs. Pass ?async=true for a
        202 accepted response when you do not need to wait for the result.
      operationId: generateSessionResponse
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
        - name: async
          in: query
          required: false
          description: When true, generation runs in the background and 202 is returned immediately
          schema:
            type: boolean
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateSessionRequest'
      responses:
        '200':
          description: Agent reply or requires_action
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenerateSessionResponse'
        '202':
          description: Generation accepted (async mode)
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [accepted]
                  session_id:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Generation already in progress
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/sessions/{session_id}/tool-outputs:
    post:
      tags:
        - Sessions
      summary: Submit tool outputs
      description: >
        Submits client tool outputs for a generation that returned
        requires_action. The agent continues its loop and returns the
        final or next requires_action result.
      operationId: submitSessionToolOutputs
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitSessionToolOutputsRequest'
      responses:
        '200':
          description: Generation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSessionMessageResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/agents/{agent_id}/sessions/{session_id}/tags:
    get:
      tags:
        - Sessions
      summary: Get session tags
      description: Returns the session's tags object.
      operationId: getSessionTags
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session tags
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
        - Sessions
      summary: Replace session tags
      description: Replaces all tags on the session.
      operationId: replaceSessionTags
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        '200':
          description: Updated tags
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Sessions
      summary: Merge session tags
      description: Merges the provided tags into the session's existing tags.
      operationId: mergeSessionTags
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        '200':
          description: Updated tags
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token or sk_ api key

  parameters:
    AgentId:
      name: agent_id
      in: path
      required: true
      description: Agent public ID
      schema:
        type: string
        example: agt_V1StGXR8Z5jdHi6B
    SessionId:
      name: session_id
      in: path
      required: true
      description: Session public ID
      schema:
        type: string
        example: sess_V1StGXR8Z5jdHi6B

  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
      required:
        - error

    SessionRecord:
      type: object
      properties:
        id:
          type: string
          description: Session public ID
          example: sess_V1StGXR8Z5jdHi6B
        agent_id:
          type: string
          description: Agent public ID
          example: agt_V1StGXR8Z5jdHi6B
        conversation_id:
          type: string
          description: Underlying conversation public ID
          example: conv_V1StGXR8Z5jdHi6B
        status:
          type: string
          enum: [open, closed]
          example: open
        name:
          type: string
          nullable: true
          example: Support chat
        actor_id:
          type: string
          nullable: true
          description: Public ID of the user actor
          example: actr_V1StGXR8Z5jdHi6B
        tags:
          type: object
          additionalProperties:
            type: string
        auto_generate:
          type: boolean
          default: false
          description: When true, automatically triggers generation after each user message (if no generation is in progress).
        generating_at:
          type: string
          format: date-time
          nullable: true
          description: Timestamp when the current generation started, or null if not generating.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Key-value pairs injected as context headers into all tool call requests made during this session.

    SessionMessage:
      type: object
      properties:
        role:
          type: string
          enum: [user, assistant, unknown]
        content:
          type: string
        document_id:
          type: string
          nullable: true
        position:
          type: integer
        metadata:
          type: object
          nullable: true

    CreateSessionRequest:
      type: object
      properties:
        name:
          type: string
          description: Optional session name
          example: Support chat
        actor_id:
          type: string
          description: Optional public ID of an existing actor to use as the user actor
          example: actr_V1StGXR8Z5jdHi6B
        auto_generate:
          type: boolean
          default: false
          description: When true, automatically triggers generation after each user message.
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Key-value pairs injected as context headers into all tool call requests made during this session.

    UpdateSessionRequest:
      type: object
      properties:
        name:
          type: string
          nullable: true
          description: Session name (set to null to clear)
        status:
          type: string
          enum: [open, closed]
          description: Session status
        auto_generate:
          type: boolean
          description: Enable or disable automatic generation after user messages.
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Key-value pairs injected as context headers into all tool call requests made during this session.

    AddSessionMessageRequest:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: User message text
          example: Hello, how can I deploy my app?
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Key-value pairs injected as context headers into all tool call requests made during this generation.

    AddSessionMessageSaved:
      type: object
      description: Message saved; auto-generate is off or a generation is already in progress.
      properties:
        role:
          type: string
          enum: [user]
        content:
          type: string
    AddSessionMessageResponse:
      oneOf:
        - $ref: '#/components/schemas/AddSessionMessageSaved'
        - $ref: '#/components/schemas/GenerateSessionResponse'

    GenerateSessionRequest:
      type: object
      properties:
        model:
          type: string
          description: Optional model override
          example: gpt-4o
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Key-value pairs injected as context headers into all tool call requests made during this generation.

    GenerateSessionResponse:
      type: object
      properties:
        status:
          type: string
          enum: [completed, requires_action]
        message:
          type: object
          properties:
            role:
              type: string
            content:
              type: string
            model:
              type: string
        generation_id:
          type: string
        trace_id:
          type: string
        required_action:
          type: object
          description: Present when status is requires_action
          properties:
            tool_calls:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  arguments:
                    type: object

    SendSessionMessageResponse:
      type: object
      properties:
        status:
          type: string
          enum: [completed, requires_action]
        message:
          type: object
          properties:
            role:
              type: string
            content:
              type: string
            model:
              type: string
        generation_id:
          type: string
        trace_id:
          type: string
        required_action:
          type: object
          description: Present when status is requires_action
          properties:
            tool_calls:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  arguments:
                    type: object

    SubmitSessionToolOutputsRequest:
      type: object
      required:
        - generation_id
        - tool_outputs
      properties:
        generation_id:
          type: string
          description: The generation ID from the requires_action response
        tool_outputs:
          type: array
          items:
            type: object
            required:
              - tool_call_id
              - output
            properties:
              tool_call_id:
                type: string
              output:
                description: The tool output value

  responses:
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
