openapi: "3.1.0"

info:
  title: "Forge — Support & Agent Execution API"
  version: "0.1.0"
  description: |
    The Forge support service is the assistance and agent-execution surface of the APEX
    platform. It answers in-application support questions with session continuity, exposes the
    APEX product tool catalog for direct invocation and goal-driven agent loops, runs configured
    AI agents with tool access and cost accounting in South African Rand, tracks
    client-executed pipeline runs for portal visibility, processes bulk jobs through the model
    provider's asynchronous batch lane, and generates entity proposal documents.

    The support service is in Preview and currently available in local/companion deployments;
    cloud availability is operator-arranged.

    **Authentication.** Every operation except `GET /health` requires a tenant-scoped
    support-service token issued by your platform operator, presented as a bearer token. Your
    tenant is resolved from the token — if you also send an `X-Tenant-Id` header it must match
    the token's tenant. The service answers `503` when it has not been configured with
    credentials.

    **Subscription tiers.** Capability is gated by your subscription tier — `starter`,
    `professional` or `enterprise`. The tool catalog is filtered to your tier, and the
    goal-driven agent loop requires `professional` or above.

servers:
  - url: http://localhost:9142
    description: Local

tags:
  - name: Support
    description: >-
      Conversational support queries with session continuity. Answers are grounded in the
      knowledge indexed for your tenant and may carry references into your application's
      source index.
  - name: Tools
    description: >-
      The APEX product tool catalog. List the tools available to your tier, invoke a single
      tool directly, or hand a goal to the agent loop and let the model compose tool calls.
  - name: Agent Execution
    description: >-
      Run configured AI agents — system prompt, tools, extended thinking — with per-call token
      usage and Rand cost accounting.
  - name: Pipeline Tracking
    description: >-
      Your application executes the pipeline; Forge tracks state for portal visibility, cost
      roll-up and audit.
  - name: Batch Processing
    description: >-
      Bulk processing through the model provider's asynchronous batch lane — create a job,
      submit it, poll for results.
  - name: Proposals
    description: Asynchronous generation of entity proposal documents, delivered as PDF.
  - name: Service
    description: Service liveness.

security:
  - bearerAuth: []

paths:
  # ── Service ────────────────────────────────────────────────────────────────

  /health:
    get:
      operationId: getHealth
      tags: [Service]
      summary: Service liveness
      x-apex-availability: preview
      description: Unauthenticated liveness probe.
      security: []
      responses:
        "200":
          description: Service is up.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  service:
                    type: string
                    example: forge-support
                  version:
                    type: string
                    example: "0.1.0"

  # ── Support ────────────────────────────────────────────────────────────────

  /api/v1/query:
    post:
      operationId: submitSupportQuery
      tags: [Support]
      summary: Ask a support question
      x-apex-availability: preview
      description: >-
        Submits a support message. Omit `sessionId` to start a new session (the response
        carries the identifier to reuse on follow-up turns); include it to continue an existing
        conversation. Optional `context` describes where the user is in your application, which
        improves answer quality. The assistant's reply is returned as a message whose
        `metadata` describes how the answer was produced, including any references into your
        application's source index.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequest"
            example:
              message: "The verification screen shows 'evidence pending' — what does that mean?"
              context:
                route: "/verifications/123"
                pageTitle: "Verification Detail"
                userRole: analyst
      responses:
        "200":
          description: Assistant reply.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueryResponse"
              example:
                sessionId: "0f8c1f9a-2f64-4c8e-9d1c-6a5b2e7d4f10"
                message:
                  id: "4c7a9d2e-8b13-4f6a-a0e5-91d2c3b4a5f6"
                  sessionId: "0f8c1f9a-2f64-4c8e-9d1c-6a5b2e7d4f10"
                  role: assistant
                  content: "'Evidence pending' means the claim has been registered but its supporting evidence has not yet been retrieved…"
                  timestamp: "2026-08-06T09:14:22.000Z"
                  metadata:
                    pipeline: "A"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/sessions/{sessionId}:
    get:
      operationId: getSupportSession
      tags: [Support]
      summary: Get a support session
      x-apex-availability: preview
      description: Returns a session's status and turn count. Sessions belong to your tenant.
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: The session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SupportSession"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/sessions/{sessionId}/messages:
    get:
      operationId: listSupportSessionMessages
      tags: [Support]
      summary: List a session's messages
      x-apex-availability: preview
      description: Returns the full message history of a session, oldest first.
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: Message history.
          content:
            application/json:
              schema:
                type: object
                required: [messages]
                properties:
                  messages:
                    type: array
                    items:
                      $ref: "#/components/schemas/SupportMessage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/sessions/{sessionId}/end:
    post:
      operationId: endSupportSession
      tags: [Support]
      summary: End a support session
      x-apex-availability: preview
      description: Marks a session as ended. Subsequent queries start a new session.
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: Session ended.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ended
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  # ── Tools ──────────────────────────────────────────────────────────────────

  /api/v1/tools:
    get:
      operationId: listTools
      tags: [Tools]
      summary: List available tools
      x-apex-availability: preview
      description: >-
        Returns the tool manifest filtered to your subscription tier. Each entry describes one
        APEX product capability — its name, the product that serves it, the JSON Schema of its
        input, and the minimum tier required to invoke it.
      responses:
        "200":
          description: Tier-filtered tool manifest.
          content:
            application/json:
              schema:
                type: object
                required: [tools, count, tier]
                properties:
                  tools:
                    type: array
                    items:
                      $ref: "#/components/schemas/ToolManifestEntry"
                  count:
                    type: integer
                    description: Number of tools available to your tier.
                  tier:
                    $ref: "#/components/schemas/Tier"
              example:
                tools:
                  - name: zenith.search
                    description: Search the tenant's ingested knowledge base.
                    product: zenith
                    inputSchema:
                      type: object
                      properties:
                        query:
                          type: string
                      required: [query]
                    minTier: starter
                count: 1
                tier: professional
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/tools/invoke:
    post:
      operationId: invokeTool
      tags: [Tools]
      summary: Invoke a single tool
      x-apex-availability: preview
      description: >-
        Invokes one tool from the manifest and returns the product's response verbatim,
        together with the upstream HTTP status. Use `pathParams` to fill placeholders in tools
        whose endpoints take path parameters.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ToolInvokeRequest"
            example:
              tool: zenith.search
              input:
                query: "supplier onboarding checklist"
      responses:
        "200":
          description: Tool invocation result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolInvokeResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The tool requires a higher subscription tier.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TierError"
              example:
                error: "Tool \"pulse.simulate\" requires enterprise tier or above"
                currentTier: professional
        "404":
          description: Unknown tool name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Unknown tool: zenith.serch"
        "502":
          description: The product serving the tool could not be reached.
          content:
            application/json:
              schema:
                type: object
                properties:
                  tool:
                    type: string
                  product:
                    type: string
                  error:
                    type: string
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/tools/agent:
    post:
      operationId: runToolAgent
      tags: [Tools]
      summary: Run a goal-driven agent loop
      x-apex-availability: preview
      description: >-
        Hands a goal to the model, which plans and executes tool calls from your tier's
        manifest until it can answer or the turn budget is exhausted. Restrict the usable tools
        with `allowedTools`, and inject domain context with `context`. Requires the
        `professional` tier or above.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ToolAgentRequest"
            example:
              goal: "Assess the supplier 'Acme Water Works': search our knowledge base for prior engagements and score their risk."
              maxTurns: 6
      responses:
        "200":
          description: >-
            Final answer with the full tool-call trace. When the turn budget is exhausted
            before the model finishes, `maxTurnsReached` is true and the answer summarises the
            partial results.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolAgentResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Agent mode requires the `professional` tier or above.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TierError"
              example:
                error: "Agent mode requires Professional tier or above"
                currentTier: starter
        "503":
          $ref: "#/components/responses/NotConfigured"

  # ── Agent Execution ────────────────────────────────────────────────────────

  /api/v1/agents/execute:
    post:
      operationId: executeAgent
      tags: [Agent Execution]
      summary: Execute a configured agent
      x-apex-availability: preview
      description: >-
        Runs one agent: your system prompt and user message, an optional tool set, optional
        extended thinking, and a bounded tool-use loop. Tools may be APEX registry shorthands
        (for example `zenith_search`, `vector_verify`, `pulse_scan`, `vector_risk_score`), the
        `web_search` server tool, or inline definitions your application handles. APEX tool
        calls are dispatched to the owning products under your tenant, and their cost is
        reported per call in `apex_costs`. If the agent wraps its final answer in
        `<output>` tags containing JSON, the parsed value is returned in `result`; otherwise
        `result` carries the text.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgentExecuteRequest"
            example:
              agent_id: supplier-profile-v1
              agent_name: Supplier Profiler
              system_prompt: "You are a due-diligence analyst. Use the tools to gather evidence, then emit a JSON profile inside <output> tags."
              user_message: "Profile the supplier 'Acme Water Works' for a bulk-water tender."
              tools: [zenith_search, vector_risk_score, web_search]
              thinking:
                enabled: true
                budget_tokens: 8000
              max_tokens: 8192
              max_tool_iterations: 3
              metadata:
                venture: your-tenant
                phase: shortlist
      responses:
        "200":
          description: Agent execution result with usage, sources and cost accounting.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentExecutionResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          description: Agent execution failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  message:
                    type: string
                  agent_id:
                    type: string
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/agents/batch:
    post:
      operationId: executeAgentBatch
      tags: [Agent Execution]
      summary: Execute agents in parallel
      x-apex-availability: preview
      description: >-
        Runs 1–10 agents concurrently. Results are returned in request order; an agent that
        fails contributes `{agent_id, error}` in its slot without failing the batch.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agents]
              properties:
                agents:
                  type: array
                  minItems: 1
                  maxItems: 10
                  items:
                    $ref: "#/components/schemas/AgentExecuteRequest"
      responses:
        "200":
          description: Per-agent results in request order.
          content:
            application/json:
              schema:
                type: object
                required: [results]
                properties:
                  results:
                    type: array
                    items:
                      oneOf:
                        - $ref: "#/components/schemas/AgentExecutionResult"
                        - type: object
                          required: [agent_id, error]
                          properties:
                            agent_id:
                              type: string
                            error:
                              type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  # ── Pipeline Tracking ──────────────────────────────────────────────────────

  /api/v1/pipelines/templates:
    post:
      operationId: createPipelineTemplate
      tags: [Pipeline Tracking]
      summary: Register a pipeline template
      x-apex-availability: preview
      description: >-
        Registers a template describing the stages, agents and approval gates of a pipeline
        your application executes. Templates give runs a stable shape for portal display and
        audit.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PipelineTemplateRequest"
            example:
              name: Proposal drafting
              description: Research, draft and review a proposal.
              stages:
                - stage_id: research
                  name: Research
                  agents:
                    - agent_id: researcher
                      system_prompt: "Gather evidence for the proposal."
                      tools: [zenith_search]
                - stage_id: review
                  name: Review
                  agents:
                    - agent_id: reviewer
                      system_prompt: "Review the draft for accuracy."
                  gate:
                    required: true
                    approvers: [reviews@your-tenant.example]
      responses:
        "201":
          description: Template created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineTemplate"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"
    get:
      operationId: listPipelineTemplates
      tags: [Pipeline Tracking]
      summary: List pipeline templates
      x-apex-availability: preview
      description: Lists your tenant's templates, newest first. Deleted templates are excluded.
      responses:
        "200":
          description: Templates.
          content:
            application/json:
              schema:
                type: object
                required: [templates, total]
                properties:
                  templates:
                    type: array
                    items:
                      $ref: "#/components/schemas/PipelineTemplate"
                  total:
                    type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/pipelines/templates/{id}:
    get:
      operationId: getPipelineTemplate
      tags: [Pipeline Tracking]
      summary: Get a pipeline template
      x-apex-availability: preview
      parameters:
        - $ref: "#/components/parameters/TemplateId"
      responses:
        "200":
          description: The template.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineTemplate"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"
    put:
      operationId: updatePipelineTemplate
      tags: [Pipeline Tracking]
      summary: Update a pipeline template
      x-apex-availability: preview
      description: Replaces the template definition. Send the full template body.
      parameters:
        - $ref: "#/components/parameters/TemplateId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PipelineTemplateRequest"
      responses:
        "200":
          description: Updated template.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineTemplate"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"
    delete:
      operationId: deletePipelineTemplate
      tags: [Pipeline Tracking]
      summary: Delete a pipeline template
      x-apex-availability: preview
      description: Soft-deletes the template. Existing runs keep their history.
      parameters:
        - $ref: "#/components/parameters/TemplateId"
      responses:
        "200":
          description: Template deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    example: true
                  id:
                    type: string
                    format: uuid
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/pipelines/runs:
    post:
      operationId: createPipelineRun
      tags: [Pipeline Tracking]
      summary: Start tracking a pipeline run
      x-apex-availability: preview
      description: >-
        Registers a run of a template. The run starts in `pending`; your application reports
        progress with `PATCH /api/v1/pipelines/runs/{id}` as it executes the stages.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PipelineRunCreateRequest"
            example:
              template_id: "8a1f4c2e-0d5b-4e7a-9c3f-6b2d8e1a4f70"
              run_name: "Acme Water Works proposal"
              input:
                entity: Acme Water Works
      responses:
        "201":
          description: Run registered.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineRun"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: The referenced template does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/NotConfigured"
    get:
      operationId: listPipelineRuns
      tags: [Pipeline Tracking]
      summary: List pipeline runs
      x-apex-availability: preview
      description: >-
        Lists your tenant's runs, newest first, optionally filtered by status. At most the 100
        most recent runs are returned.
      parameters:
        - name: status
          in: query
          required: false
          description: Filter by run status.
          schema:
            $ref: "#/components/schemas/PipelineRunStatus"
      responses:
        "200":
          description: Runs.
          content:
            application/json:
              schema:
                type: object
                required: [runs, total]
                properties:
                  runs:
                    type: array
                    items:
                      $ref: "#/components/schemas/PipelineRun"
                  total:
                    type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/pipelines/runs/{id}:
    get:
      operationId: getPipelineRun
      tags: [Pipeline Tracking]
      summary: Get run state and cost roll-up
      x-apex-availability: preview
      description: >-
        Returns the run — status, accumulated agent results, total cost and duration — together
        with its gate decision history.
      parameters:
        - $ref: "#/components/parameters/RunId"
      responses:
        "200":
          description: Run detail with gates.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PipelineRun"
                  - type: object
                    properties:
                      gates:
                        type: array
                        items:
                          $ref: "#/components/schemas/PipelineGateRecord"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"
    patch:
      operationId: updatePipelineRun
      tags: [Pipeline Tracking]
      summary: Report run progress
      x-apex-availability: preview
      description: >-
        Reports progress from your application's execution of the run: the current status,
        stage and agent, and per-agent results as they complete. Each reported `agent_result`
        is appended to the run's history, and its `cost_zar` and `duration_ms` accumulate into
        the run's `total_cost_zar` and `total_duration_ms`.
      parameters:
        - $ref: "#/components/parameters/RunId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PipelineRunUpdateRequest"
            example:
              status: running
              current_stage: research
              current_agent: researcher
              agent_result:
                agent_id: researcher
                stage_id: research
                status: completed
                cost_zar: 1.84
                duration_ms: 12400
                model: claude-sonnet-4-6
      responses:
        "200":
          description: Updated run.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineRun"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/pipelines/runs/{id}/gates/{gateId}:
    post:
      operationId: recordGateDecision
      tags: [Pipeline Tracking]
      summary: Record a gate decision
      x-apex-availability: preview
      description: >-
        Records an approval decision for a gate on this run, for the audit trail. `approved`
        moves the run back to `running`; `rejected` moves it to `failed`; `deferred` leaves the
        run state unchanged.
      parameters:
        - $ref: "#/components/parameters/RunId"
        - name: gateId
          in: path
          required: true
          description: Gate identifier within the run's template.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GateDecisionRequest"
            example:
              decision: approved
              decided_by: reviews@your-tenant.example
              notes: "Evidence checks out."
      responses:
        "201":
          description: Recorded gate decision.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineGateRecord"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  # ── Batch Processing ───────────────────────────────────────────────────────

  /api/v1/batch:
    post:
      operationId: createBatchJob
      tags: [Batch Processing]
      summary: Create a batch job
      x-apex-availability: preview
      description: >-
        Creates a bulk-processing job of a given type — `verification` (claim checking),
        `rag_query` (retrieval-grounded questions) or `summarization`. Set `autoSubmit` to
        create and submit in one call; otherwise submit later with
        `POST /api/v1/batch/{id}/submit`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchCreateRequest"
            example:
              type: summarization
              items:
                - customId: doc-001
                  content: "Full text of the first document…"
                - customId: doc-002
                  content: "Full text of the second document…"
              autoSubmit: true
      responses:
        "201":
          description: Created job (submitted when `autoSubmit` was set).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJob"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"
    get:
      operationId: listBatchJobs
      tags: [Batch Processing]
      summary: List batch jobs
      x-apex-availability: preview
      description: Lists your tenant's jobs as summaries with item and result counts.
      responses:
        "200":
          description: Job summaries.
          content:
            application/json:
              schema:
                type: object
                required: [jobs, total]
                properties:
                  jobs:
                    type: array
                    items:
                      $ref: "#/components/schemas/BatchJobSummary"
                  total:
                    type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/batch/{id}:
    get:
      operationId: getBatchJob
      tags: [Batch Processing]
      summary: Get a batch job
      x-apex-availability: preview
      description: >-
        Returns the job with its items and any results gathered so far. Jobs outside your
        tenant are reported as not found.
      parameters:
        - $ref: "#/components/parameters/BatchJobId"
      responses:
        "200":
          description: The job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJob"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/batch/{id}/submit:
    post:
      operationId: submitBatchJob
      tags: [Batch Processing]
      summary: Submit a pending batch job
      x-apex-availability: preview
      description: Submits a pending job to the provider's batch lane for processing.
      parameters:
        - $ref: "#/components/parameters/BatchJobId"
      responses:
        "200":
          description: The submitted job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJob"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/batch/{id}/poll:
    post:
      operationId: pollBatchJob
      tags: [Batch Processing]
      summary: Poll a batch job for results
      x-apex-availability: preview
      description: >-
        Checks the provider for progress and pulls in any completed results. Call periodically
        until the job status is `completed`, `failed` or `cancelled`.
      parameters:
        - $ref: "#/components/parameters/BatchJobId"
      responses:
        "200":
          description: The job with current status and results.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJob"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/batch/{id}/cancel:
    post:
      operationId: cancelBatchJob
      tags: [Batch Processing]
      summary: Cancel a batch job
      x-apex-availability: preview
      parameters:
        - $ref: "#/components/parameters/BatchJobId"
      responses:
        "200":
          description: The cancelled job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJob"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/batch/config/{type}:
    get:
      operationId: getBatchDefaultConfig
      tags: [Batch Processing]
      summary: Get default configuration for a job type
      x-apex-availability: preview
      description: >-
        Returns the default processing configuration applied to jobs of the given type. Values
        can be overridden per job via the `config` field at creation.
      parameters:
        - name: type
          in: path
          required: true
          schema:
            $ref: "#/components/schemas/BatchJobType"
      responses:
        "200":
          description: Default configuration.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJobConfig"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  # ── Proposals ──────────────────────────────────────────────────────────────

  /api/v1/proposals/generate:
    post:
      operationId: generateProposal
      tags: [Proposals]
      summary: Start proposal generation
      x-apex-availability: preview
      description: >-
        Starts asynchronous generation of a proposal document for an entity. The call answers
        immediately with `202` and a proposal identifier; poll
        `GET /api/v1/proposals/{id}/status` until the status is `complete`, then fetch the PDF
        from `GET /api/v1/proposals/{id}/download`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalGenerateRequest"
            example:
              entityName: Acme Water Works
              requestedBy: analyst@your-tenant.example
      responses:
        "202":
          description: Generation started.
          content:
            application/json:
              schema:
                type: object
                required: [proposalId, status]
                properties:
                  proposalId:
                    type: string
                  status:
                    $ref: "#/components/schemas/ProposalStatus"
                  entityName:
                    type: string
                  template:
                    type: string
                  createdAt:
                    type: string
                    format: date-time
              example:
                proposalId: "c2d4e6f8-1a3b-4c5d-8e9f-0a1b2c3d4e5f"
                status: pending
                entityName: Acme Water Works
                template: entity-summary
                createdAt: "2026-08-06T09:30:00.000Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/proposals/{id}/status:
    get:
      operationId: getProposalStatus
      tags: [Proposals]
      summary: Poll proposal generation status
      x-apex-availability: preview
      parameters:
        - $ref: "#/components/parameters/ProposalId"
      responses:
        "200":
          description: Generation progress.
          content:
            application/json:
              schema:
                type: object
                required: [id, entityName, template, status]
                properties:
                  id:
                    type: string
                  entityName:
                    type: string
                  template:
                    type: string
                  status:
                    $ref: "#/components/schemas/ProposalStatus"
                  errorMessage:
                    type: ["string", "null"]
                    description: Failure detail when status is `failed`.
                  createdAt:
                    type: string
                    format: date-time
                  updatedAt:
                    type: string
                    format: date-time
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/NotConfigured"

  /api/v1/proposals/{id}/download:
    get:
      operationId: downloadProposal
      tags: [Proposals]
      summary: Download the generated proposal PDF
      x-apex-availability: preview
      description: >-
        Serves the completed proposal as a PDF attachment. Answers `409` while generation is
        still in progress or has failed.
      parameters:
        - $ref: "#/components/parameters/ProposalId"
      responses:
        "200":
          description: The proposal document.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: The proposal is not ready for download.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  status:
                    $ref: "#/components/schemas/ProposalStatus"
              example:
                error: Proposal not ready
                status: generating
        "503":
          $ref: "#/components/responses/NotConfigured"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Tenant-scoped support-service token issued by your platform operator. Your tenant is
        resolved from the token; an `X-Tenant-Id` header, if supplied, must match it. The
        service answers `503` when it has not been configured with credentials.

  parameters:
    SessionId:
      name: sessionId
      in: path
      required: true
      description: Support session identifier.
      schema:
        type: string
    TemplateId:
      name: id
      in: path
      required: true
      description: Pipeline template identifier.
      schema:
        type: string
        format: uuid
    RunId:
      name: id
      in: path
      required: true
      description: Pipeline run identifier.
      schema:
        type: string
        format: uuid
    BatchJobId:
      name: id
      in: path
      required: true
      description: Batch job identifier.
      schema:
        type: string
    ProposalId:
      name: id
      in: path
      required: true
      description: Proposal identifier.
      schema:
        type: string

  schemas:
    Tier:
      type: string
      description: Subscription tier.
      enum: [starter, professional, enterprise]

    Error:
      type: object
      description: Error envelope.
      properties:
        error:
          type: string
      additionalProperties: true

    ValidationError:
      type: object
      description: Request validation failure with per-field issues.
      properties:
        error:
          type: string
          example: Invalid request
        details:
          type: array
          description: Validation issues.
          items:
            type: object
            additionalProperties: true

    TierError:
      type: object
      description: The operation or tool requires a higher subscription tier.
      properties:
        error:
          type: string
        currentTier:
          $ref: "#/components/schemas/Tier"

    # ── Support ──────────────────────────────────────────────────────────────

    QueryRequest:
      type: object
      required: [message]
      properties:
        sessionId:
          type: string
          description: Existing session to continue. Omit to start a new session.
        message:
          type: string
          minLength: 1
          maxLength: 4000
          description: The user's support question.
        context:
          type: object
          description: Where the user is in your application.
          properties:
            route:
              type: string
              description: Application route the user is on.
            pageTitle:
              type: string
            userRole:
              type: string
            visibleError:
              type: string
              description: Error text currently visible to the user, if any.
            custom:
              type: object
              description: Additional key-value context.
              additionalProperties:
                type: string

    QueryResponse:
      type: object
      required: [sessionId, message]
      properties:
        sessionId:
          type: string
          description: Session identifier — reuse on follow-up turns.
        message:
          $ref: "#/components/schemas/SupportMessage"
        suggestedActions:
          type: array
          description: Optional follow-up actions your interface can offer.
          items:
            type: object
            properties:
              label:
                type: string
              type:
                type: string
                enum: [navigate, retry, escalate, link]
              payload:
                type: string

    SupportSession:
      type: object
      required: [id, tenantId, startedAt, status, turnCount]
      properties:
        id:
          type: string
        tenantId:
          type: string
          example: your-tenant
        userId:
          type: string
        startedAt:
          type: string
          format: date-time
        status:
          type: string
          enum: [active, resolved, escalated]
        turnCount:
          type: integer

    SupportMessage:
      type: object
      required: [id, sessionId, role, content, timestamp]
      properties:
        id:
          type: string
        sessionId:
          type: string
        role:
          type: string
          enum: [user, assistant, system]
        content:
          type: string
        timestamp:
          type: string
          format: date-time
        metadata:
          $ref: "#/components/schemas/SupportMessageMetadata"

    SupportMessageMetadata:
      type: object
      description: How an assistant reply was produced. Present on assistant messages.
      x-apex-note: "Schema partially documented — verify against the service."
      properties:
        pipeline:
          type: string
          description: Which answering pipeline produced the reply.
        pipelineReason:
          type: string
          description: Why that pipeline was selected.
        intent:
          type: string
          description: Classified intent of the user's question.
        confidence:
          type: number
          description: Confidence score between 0 and 1.
        codeReferences:
          type: array
          description: References into your application's source index that grounded the answer.
          items:
            type: object
            required: [filePath, summary]
            properties:
              filePath:
                type: string
              startLine:
                type: integer
              endLine:
                type: integer
              componentName:
                type: string
              summary:
                type: string
        guardrailFlags:
          type: array
          description: Content-guardrail flags raised on the reply, when any.
          items:
            type: string
      additionalProperties: true

    # ── Tools ────────────────────────────────────────────────────────────────

    ToolManifestEntry:
      type: object
      required: [name, description, product, inputSchema, minTier]
      properties:
        name:
          type: string
          description: Tool name to use in invocations.
          example: zenith.search
        description:
          type: string
        product:
          type: string
          description: APEX product that serves the tool.
          example: zenith
        inputSchema:
          type: object
          description: JSON Schema of the tool's input.
          additionalProperties: true
        minTier:
          $ref: "#/components/schemas/Tier"

    ToolInvokeRequest:
      type: object
      required: [tool, input]
      properties:
        tool:
          type: string
          description: Tool name from the manifest.
        input:
          type: object
          description: Input matching the tool's `inputSchema`.
          additionalProperties: true
        pathParams:
          type: object
          description: Values for path placeholders in the tool's endpoint, when it has any.
          additionalProperties:
            type: string

    ToolInvokeResponse:
      type: object
      required: [tool, product, status, result]
      properties:
        tool:
          type: string
        product:
          type: string
        status:
          type: integer
          description: HTTP status returned by the product that served the tool.
        result:
          description: The product's response body, verbatim.

    ToolAgentRequest:
      type: object
      required: [goal]
      properties:
        goal:
          type: string
          minLength: 1
          maxLength: 8000
          description: What the agent should accomplish.
        allowedTools:
          type: array
          description: Restrict the agent to these tool names. Defaults to all tools in your tier.
          items:
            type: string
        maxTurns:
          type: integer
          minimum: 1
          maximum: 25
          default: 10
          description: Maximum tool-use turns before a final answer is forced.
        context:
          type: string
          description: Domain context injected into the agent's instructions.

    ToolAgentResponse:
      type: object
      required: [answer, toolCalls, turns, model]
      properties:
        answer:
          type: string
          description: The agent's final answer.
        toolCalls:
          type: array
          description: Every tool call made, in order.
          items:
            type: object
            required: [tool, input, output]
            properties:
              tool:
                type: string
              input:
                description: Input the model supplied.
              output:
                description: Result the tool returned.
        turns:
          type: integer
        model:
          type: string
        maxTurnsReached:
          type: boolean
          description: True when the turn budget was exhausted before the model finished.

    # ── Agent Execution ──────────────────────────────────────────────────────

    AgentToolReference:
      description: >-
        A tool available to the agent — either a registry name (`zenith_search`, `pulse_scan`,
        `vector_verify`, `vector_risk_score`, or a manifest tool name), the `web_search` server
        tool, or an inline tool definition your application handles.
      oneOf:
        - type: string
          description: Registry tool name or `web_search`.
          examples: [zenith_search, vector_verify, web_search]
        - type: object
          required: [name, description, input_schema]
          properties:
            name:
              type: string
            description:
              type: string
            input_schema:
              type: object
              description: JSON Schema of the tool's input.
              additionalProperties: true

    AgentExecuteRequest:
      type: object
      required: [agent_id, system_prompt, user_message]
      properties:
        agent_id:
          type: string
          minLength: 1
          maxLength: 128
          description: Your identifier for this agent, used in tracking and audit.
        agent_name:
          type: string
          maxLength: 256
          description: Display name.
        system_prompt:
          type: string
          minLength: 1
          maxLength: 100000
          description: The agent's role and instructions.
        user_message:
          type: string
          minLength: 1
          maxLength: 500000
          description: Task input — context, previous outputs, instructions.
        model:
          type: string
          maxLength: 128
          description: >-
            Model to use. Defaults to the deployment's configured agent model
            (`claude-sonnet-4-6` when not configured).
        tools:
          type: array
          description: Tools the agent may call.
          default: []
          items:
            $ref: "#/components/schemas/AgentToolReference"
        thinking:
          type: object
          description: Extended-thinking configuration.
          required: [enabled]
          properties:
            enabled:
              type: boolean
            budget_tokens:
              type: integer
              minimum: 1024
              maximum: 128000
              default: 10000
        max_tokens:
          type: integer
          minimum: 256
          maximum: 128000
          default: 16384
          description: Maximum output tokens per model turn.
        max_tool_iterations:
          type: integer
          minimum: 0
          maximum: 10
          default: 3
          description: Maximum tool-use loop iterations.
        metadata:
          type: object
          description: Key-value metadata attached for cost tracking — phase, venture, and so on.
          additionalProperties:
            type: string

    AgentUsage:
      type: object
      description: Token usage accumulated across all model turns of the execution.
      required:
        - input_tokens
        - output_tokens
        - thinking_tokens
        - cache_creation_input_tokens
        - cache_read_input_tokens
        - web_search_requests
      properties:
        input_tokens:
          type: integer
        output_tokens:
          type: integer
        thinking_tokens:
          type: integer
        cache_creation_input_tokens:
          type: integer
        cache_read_input_tokens:
          type: integer
        web_search_requests:
          type: integer

    AgentSource:
      type: object
      description: A source the agent drew on — web search results or knowledge-base hits.
      required: [url, title, type, reliability]
      properties:
        url:
          type: string
        title:
          type: string
        snippet:
          type: string
        type:
          type: string
          description: Source kind, for example `web_search` or `zenith_search`.
        reliability:
          type: string
          description: Source reliability rating, for example `high` or `medium`.

    ApexToolCost:
      type: object
      description: Cost of one APEX tool call made during the execution.
      required: [service, operation, cost_zar, duration_ms]
      properties:
        service:
          type: string
          description: APEX product that served the call.
        operation:
          type: string
          description: Tool name as invoked.
        cost_zar:
          type: number
          description: Cost of the call in South African Rand.
        duration_ms:
          type: integer

    AgentExecutionResult:
      type: object
      required:
        - agent_id
        - result
        - raw_response
        - usage
        - tools_used
        - sources
        - apex_costs
        - model
        - duration_ms
        - tool_iterations
        - timestamp
      properties:
        agent_id:
          type: string
        agent_name:
          type: string
        result:
          description: >-
            Structured output parsed from the agent's `<output>` tags when present (JSON where
            parseable), otherwise the response text.
        raw_response:
          type: string
          description: Full text response from the model.
        thinking:
          type: string
          description: Extended-thinking content, when thinking was enabled.
        usage:
          $ref: "#/components/schemas/AgentUsage"
        tools_used:
          type: array
          description: Distinct tool names the agent called.
          items:
            type: string
        sources:
          type: array
          items:
            $ref: "#/components/schemas/AgentSource"
        apex_costs:
          type: array
          description: Per-call cost of APEX tool dispatches made during execution.
          items:
            $ref: "#/components/schemas/ApexToolCost"
        model:
          type: string
        duration_ms:
          type: integer
        tool_iterations:
          type: integer
          description: Tool-use loop iterations consumed.
        timestamp:
          type: string
          format: date-time

    # ── Pipeline Tracking ────────────────────────────────────────────────────

    PipelineAgentDefinition:
      type: object
      required: [agent_id, system_prompt]
      properties:
        agent_id:
          type: string
        name:
          type: string
        system_prompt:
          type: string
        tools:
          type: array
          default: []
          items:
            type: string
        model:
          type: string
        execution:
          type: string
          enum: [sequential, parallel]
          default: sequential

    PipelineStageDefinition:
      type: object
      required: [stage_id, name, agents]
      properties:
        stage_id:
          type: string
        name:
          type: string
        agents:
          type: array
          items:
            $ref: "#/components/schemas/PipelineAgentDefinition"
        gate:
          type: object
          description: Optional approval gate at the end of the stage.
          properties:
            required:
              type: boolean
              default: false
            approvers:
              type: array
              default: []
              items:
                type: string
            auto_approve_threshold:
              type: number
              minimum: 0
              maximum: 100

    PipelineTemplateRequest:
      type: object
      required: [name, stages]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        description:
          type: string
          default: ""
        stages:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/PipelineStageDefinition"
        tool_entitlements:
          type: array
          description: Tool names the pipeline's agents are entitled to use.
          default: []
          items:
            type: string
        metadata:
          type: object
          default: {}
          additionalProperties: true

    PipelineTemplate:
      type: object
      required: [id, tenant_id, name, stages, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          example: your-tenant
        name:
          type: string
        description:
          type: string
        stages:
          type: array
          items:
            $ref: "#/components/schemas/PipelineStageDefinition"
        tool_entitlements:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
        deleted:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    PipelineRunCreateRequest:
      type: object
      required: [template_id, run_name]
      properties:
        template_id:
          type: string
          format: uuid
        run_name:
          type: string
          minLength: 1
          maxLength: 200
        input:
          type: object
          description: Run input recorded for audit.
          default: {}
          additionalProperties: true
        metadata:
          type: object
          default: {}
          additionalProperties: true

    PipelineRunStatus:
      type: string
      description: Run lifecycle status. Runs start in `pending`.
      enum: [pending, running, paused, gated, completed, failed, cancelled]

    PipelineAgentResult:
      type: object
      description: One agent's outcome within a run.
      required: [agent_id, stage_id, status]
      properties:
        agent_id:
          type: string
        stage_id:
          type: string
        status:
          type: string
          enum: [completed, failed, skipped]
        cost_zar:
          type: number
          default: 0
        duration_ms:
          type: integer
          default: 0
        model:
          type: string
        metadata:
          type: object
          default: {}
          additionalProperties: true

    PipelineRun:
      type: object
      required: [id, tenant_id, template_id, run_name, status, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          example: your-tenant
        template_id:
          type: string
          format: uuid
        run_name:
          type: string
        status:
          $ref: "#/components/schemas/PipelineRunStatus"
        current_stage:
          type: ["string", "null"]
        current_agent:
          type: ["string", "null"]
        input:
          type: object
          additionalProperties: true
        metadata:
          type: object
          additionalProperties: true
        total_cost_zar:
          type: number
          description: Accumulated cost of reported agent results, in South African Rand.
        total_duration_ms:
          type: integer
          description: Accumulated duration of reported agent results.
        agent_results:
          type: array
          items:
            $ref: "#/components/schemas/PipelineAgentResult"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    PipelineRunUpdateRequest:
      type: object
      description: Progress report. All fields optional; send what changed.
      properties:
        status:
          type: string
          enum: [running, paused, gated, completed, failed, cancelled]
        current_stage:
          type: string
        current_agent:
          type: string
        agent_result:
          $ref: "#/components/schemas/PipelineAgentResult"

    GateDecisionRequest:
      type: object
      required: [decision, decided_by]
      properties:
        decision:
          type: string
          enum: [approved, rejected, deferred]
        decided_by:
          type: string
          description: Who made the decision.
        notes:
          type: string
          default: ""

    PipelineGateRecord:
      type: object
      required: [id, run_id, gate_id, stage_id, created_at]
      properties:
        id:
          type: string
          format: uuid
        run_id:
          type: string
          format: uuid
        gate_id:
          type: string
        stage_id:
          type: string
        decision:
          type: ["string", "null"]
          description: "`approved`, `rejected` or `deferred` once decided."
        decided_by:
          type: ["string", "null"]
        notes:
          type: string
        decided_at:
          type: ["string", "null"]
          format: date-time
        created_at:
          type: string
          format: date-time

    # ── Batch Processing ─────────────────────────────────────────────────────

    BatchJobType:
      type: string
      description: What each item is processed as.
      enum: [verification, rag_query, summarization]

    BatchJobStatus:
      type: string
      enum: [pending, processing, completed, failed, cancelled]

    BatchItem:
      type: object
      required: [customId, content]
      properties:
        customId:
          type: string
          description: Your identifier for the item — results are keyed by it.
        content:
          type: string
          description: The content to process — claim text, query text, or document text.
        context:
          type: string
          description: Optional supporting context, such as evidence or source documents.

    BatchCreateRequest:
      type: object
      required: [type, items]
      properties:
        type:
          $ref: "#/components/schemas/BatchJobType"
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/BatchItem"
        config:
          type: object
          description: >-
            Overrides of the type's default configuration
            (`GET /api/v1/batch/config/{type}`).
          x-apex-note: "Schema partially documented — verify against the service."
          additionalProperties: true
        autoSubmit:
          type: boolean
          description: Submit the job immediately after creation.

    BatchJobConfig:
      type: object
      description: Processing configuration for a batch job.
      required: [maxConcurrency, maxBatchSize, model, maxTokens]
      properties:
        maxConcurrency:
          type: integer
          description: Maximum concurrent provider batch requests per tenant.
        maxBatchSize:
          type: integer
          description: Maximum items per batch.
        model:
          type: string
          description: Model used for batch processing.
        maxTokens:
          type: integer
          description: Maximum output tokens per item.

    BatchThroughput:
      type: object
      required: [totalItems, processedItems, failedItems]
      properties:
        totalItems:
          type: integer
        processedItems:
          type: integer
        failedItems:
          type: integer
        startedAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
        itemsPerSecond:
          type: number
        estimatedCostZar:
          type: number
          description: Estimated processing cost in South African Rand.

    BatchResult:
      type: object
      required: [customId, status]
      properties:
        customId:
          type: string
        status:
          type: string
          enum: [success, error]
        content:
          type: string
          description: Processed output when status is `success`.
        error:
          type: string
          description: Failure detail when status is `error`.

    BatchJob:
      type: object
      required: [id, tenantId, type, status, items, results, config, throughput, createdAt]
      properties:
        id:
          type: string
        tenantId:
          type: string
          example: your-tenant
        type:
          $ref: "#/components/schemas/BatchJobType"
        status:
          $ref: "#/components/schemas/BatchJobStatus"
        anthropicBatchId:
          type: string
          description: Provider batch identifier, set after submission.
        items:
          type: array
          items:
            $ref: "#/components/schemas/BatchItem"
        results:
          type: array
          items:
            $ref: "#/components/schemas/BatchResult"
        config:
          $ref: "#/components/schemas/BatchJobConfig"
        throughput:
          $ref: "#/components/schemas/BatchThroughput"
        createdAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time

    BatchJobSummary:
      type: object
      description: A job without its items and results, plus counts.
      required: [id, tenantId, type, status, config, throughput, createdAt, itemCount, resultCount]
      properties:
        id:
          type: string
        tenantId:
          type: string
        type:
          $ref: "#/components/schemas/BatchJobType"
        status:
          $ref: "#/components/schemas/BatchJobStatus"
        anthropicBatchId:
          type: string
        config:
          $ref: "#/components/schemas/BatchJobConfig"
        throughput:
          $ref: "#/components/schemas/BatchThroughput"
        createdAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
        itemCount:
          type: integer
        resultCount:
          type: integer

    # ── Proposals ────────────────────────────────────────────────────────────

    ProposalStatus:
      type: string
      description: Generation lifecycle.
      enum: [pending, querying, generating, complete, failed]

    ProposalGenerateRequest:
      type: object
      required: [entityName]
      properties:
        entityName:
          type: string
          minLength: 1
          maxLength: 256
          description: Entity the proposal is about.
        template:
          type: string
          maxLength: 64
          default: entity-summary
          description: Proposal template to use.
        requestedBy:
          type: string
          maxLength: 128
          description: Who requested the proposal, for the audit trail.

  responses:
    BadRequest:
      description: The request body failed validation.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ValidationError"
    Unauthorized:
      description: Missing or invalid support-service token.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: Unauthorized
    NotFound:
      description: The resource does not exist in your tenant.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotConfigured:
      description: The service has not been configured with credentials.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: Service not configured
