openapi: 3.0.3
info:
  title: Traces API
  version: 1.0.0
  description: >
    Execution traces represent the full observability tree for agent runs.
    A trace captures one agent's execution session (potentially multiple LLM
    calls in a tool-calling loop). When agents call other agents, each gets
    its own trace linked via parentTraceId/rootTraceId, forming a tree that
    can be retrieved with the /tree endpoint.
  contact:
    name: SOAT API Support

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: Traces
    description: Inspect execution traces and trace trees

security:
  - bearerAuth: []

paths:
  /api/v1/traces:
    get:
      tags:
        - Traces
      summary: List traces
      description: Returns a paginated list of execution traces for the project.
      operationId: listTraces
      parameters:
        - name: project_id
          in: query
          required: false
          schema:
            type: string
          description: Project public ID to filter by
        - name: parent_trace_id
          in: query
          required: false
          schema:
            type: string
            example: trace_V1StGXR8Z5jdHi6B
          description: >-
            Only the direct children of this trace. A trace naming nothing the
            caller can read matches nothing rather than widening the listing.
        - $ref: './pagination.yaml#/components/parameters/Limit'
        - $ref: './pagination.yaml#/components/parameters/Offset'
      responses:
        '200':
          description: List of traces
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Trace'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'

  /api/v1/traces/{trace_id}:
    get:
      tags:
        - Traces
      summary: Get a trace
      description: Returns a single trace by ID.
      operationId: getTrace
      x-soat-resource:
        kind: trace
        from: trace_id
      parameters:
        - name: trace_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the trace
      responses:
        '200':
          description: Trace details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trace'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '404':
          description: Trace not found
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'

  /api/v1/traces/{trace_id}/tree:
    get:
      tags:
        - Traces
      summary: Get trace tree
      description: >
        Returns the full execution tree rooted at the given trace (or its root
        if the given trace is a child). Each node represents one agent's
        execution session. The `children` array contains traces triggered by
        sub-agent tool calls from that trace. A node's generations are listed
        by `GET /api/v1/generations?trace_id=`, and a node's direct children
        page by `GET /api/v1/traces?parent_trace_id=`.
      operationId: getTraceTree
      x-soat-resource:
        kind: trace
        from: trace_id
      parameters:
        - name: trace_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of any trace in the tree (root or child)
      responses:
        '200':
          description: Trace tree rooted at the resolved root trace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TraceTreeNode'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '404':
          description: Trace not found
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'

  /api/v1/traces/{trace_id}/content:
    delete:
      tags:
        - Traces
      summary: Purge trace content
      description: >
        Deletes the trace's steps object from storage and clears its content
        columns (`file_id`, `error`), cascading to every descendant trace and to
        all of their generations. A descendant holds its own steps object
        covering the same run, so the cascade is what makes the erasure
        complete rather than merely partial.


        The rows survive as auditable skeletons with `content_redacted_at` set —
        ids, timestamps, step counts, and the generations' usage-attribution
        fields are preserved, because the billing and audit ledger must outlive a
        tenant's erasure of the content. A purged trace therefore reads back as a
        skeleton, not a 404: a 404 would prove nothing.


        Idempotent — purging an already-purged trace succeeds and leaves the
        original `content_redacted_at` in place.
      operationId: purgeTraceContent
      x-soat-resource:
        kind: trace
        from: trace_id
      parameters:
        - name: trace_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the trace
      responses:
        '200':
          description: The purged trace skeleton
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trace'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '404':
          description: Trace not found
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  schemas:
    Trace:
      type: object
      properties:
        id:
          type: string
          description: Public ID of the trace
          example: trace_V1StGXR8Z5jdHi6B
        project_id:
          x-soat-ref: projects
          type: string
          description: Public ID of the project
        agent_id:
          x-soat-ref: agents
          type: string
          description: Public ID of the agent that produced this trace
        file_id:
          x-soat-ref: files
          type: string
          nullable: true
          description: >
            Public ID of the File containing the full serialized steps JSON.
            Null if the trace has not been saved yet (save is fire-and-forget).
          example: file_xyz789
        step_count:
          type: integer
          description: Number of steps recorded in this trace
          example: 2
        parent_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
          description: >
            Public ID of the parent trace. Null if this trace is the root
            (i.e., it was not triggered by a sub-agent call from another trace).
        root_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
          description: >
            Public ID of the root trace for the entire execution tree.
            Null if this trace is itself the root.
        error:
          allOf:
            - $ref: './errors.yaml#/components/schemas/StoredError'
          nullable: true
          description: >-
            Why a generation in this trace failed (e.g. an upstream AI provider
            error). Null if no failure has been recorded.
        content_redacted_at:
          type: string
          format: date-time
          nullable: true
          description: >
            When the trace's content was purged. Non-null means the steps object
            has been deleted from storage and the content columns cleared, while
            this row survives as an auditable skeleton (ids, timestamps, step
            count). A purged trace still reads back as a skeleton with this
            marker set rather than as a 404, so the erasure is provable.
        content_redacted_by_principal_type:
          nullable: true
          allOf:
            - $ref: './principals.yaml#/components/schemas/PrincipalType'
          description: >-
            Who purged the content: `user` or `api_key` for a purge request,
            `system` for the retention sweep and for content never stored under
            zero retention.
        content_redacted_by_principal_id:
          type: string
          nullable: true
          description: >-
            Public id of that principal — the API key's own id for key auth, so
            the record names which key acted. `retention_sweep` or
            `zero_retention` for `system`.
          example: user_V1StGXR8Z5jdHi6B
        created_at:
          type: string
          format: date-time

    TraceTreeNode:
      type: object
      description: A trace node in the execution tree, with nested children.
      properties:
        id:
          type: string
          description: Public ID of the trace
        project_id:
          x-soat-ref: projects
          type: string
        agent_id:
          x-soat-ref: agents
          type: string
        file_id:
          x-soat-ref: files
          type: string
          nullable: true
        step_count:
          type: integer
        parent_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
        root_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
        error:
          allOf:
            - $ref: './errors.yaml#/components/schemas/StoredError'
          nullable: true
          description: Why a generation in this trace failed
        created_at:
          type: string
          format: date-time
        content_redacted_at:
          type: string
          format: date-time
          nullable: true
          description: >
            When the trace's content was purged. Non-null means the steps object
            has been deleted from storage and the content columns cleared, while
            this row survives as an auditable skeleton (ids, timestamps, step
            count). A purged trace still reads back as a skeleton with this
            marker set rather than as a 404, so the erasure is provable.
        content_redacted_by_principal_type:
          nullable: true
          allOf:
            - $ref: './principals.yaml#/components/schemas/PrincipalType'
          description: >-
            Who purged the content: `user` or `api_key` for a purge request,
            `system` for the retention sweep and for content never stored under
            zero retention.
        content_redacted_by_principal_id:
          type: string
          nullable: true
          description: >-
            Public id of that principal — the API key's own id for key auth, so
            the record names which key acted. `retention_sweep` or
            `zero_retention` for `system`.
          example: user_V1StGXR8Z5jdHi6B
        children:
          type: array
          description: Child traces triggered by sub-agent calls from this trace
          items:
            $ref: '#/components/schemas/TraceTreeNode'
