openapi: 3.1.0
info:
  title: Bairav AI Gateway
  version: 1.0.1
  summary: An OpenAI-compatible governed proxy. Change one URL and every model call is checked against your policies.
  description: |
    The Bairav AI Gateway sits between your application and your model provider. Point your OpenAI-compatible client at the gateway URL and Bairav enforces your policies on every call, records the decision in the audit ledger and accounts for tokens and cost.

    **Your provider key stays yours.** Send your real provider key in `Authorization`. The gateway forwards it to the provider and never stores it.

    **The gateway credential is in the URL path.** A gateway key looks like `bst_<8 hex>.<secret>` and is part of every path (`/gw/{token}/...`). Treat the full base URL as a secret: it appears in access logs and shell history. Create and revoke gateway keys in the Bairav console, or with the Portal API (`POST /api/gateway/keys`, admin only).

    **How a call is decided**

    1. Pre-flight check of the request shape.
    2. Kill switch: agents that are suspended, blocked or rejected are refused.
    3. Guardrail patterns on the latest user message.
    4. Optional AI judge, which can only escalate and fails safe.
    5. The request is forwarded to your provider.
    6. Post-call checks on tool calls and the answer, then your control policies (block, require approval, flag, notify).
    7. The decision, tokens and latency are logged.

    **Blocked calls look like normal answers.** A refusal is returned as an OpenAI-shaped completion with HTTP `200` (as a stream if you asked for one) and the response header `X-Bairav-Blocked: 1`. Check that header, not just the status code.

    **The gateway fails open on its own errors.** If a governance lookup fails internally, the call degrades to a plain proxy rather than breaking your application.
  contact:
    name: Bairav developer support
    email: BTG.Support@prokraya.com
  x-source:
    repo: Bairav-MVP2
    commit: 870b92fe954c5ecc55c3ae745e2c6f1f8cf43dd1
    exportedAt: 2026-10-29T17:08:05Z
servers:
  - url: https://{host}
    description: Your Bairav deployment
    variables:
      host:
        default: your-tenant.example.com
        description: The hostname of your Bairav deployment.
tags:
  - name: Chat
    description: Governed chat completions.
  - name: Provider passthrough
    description: Other provider endpoints, forwarded as they are.
  - name: Reasoning trail
    description: Report what your agent did, and read it back.
  - name: Health
    description: Liveness.
paths:
  /gw/{token}/v1/chat/completions:
    post:
      tags:
        - Chat
      operationId: createChatCompletion
      summary: Create a governed chat completion
      description: |
        Send an OpenAI chat-completions request. The body is forwarded to your provider unchanged. Streaming (`"stream": true`) is supported.

        If the request is blocked, you receive a normal completion containing the refusal message, HTTP `200` and `X-Bairav-Blocked: 1`.

        Other outcomes: `400` when `messages` is missing, `401` for an invalid or revoked gateway key, `502` when your provider cannot be reached.
      security:
        - ProviderKey: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - $ref: "#/components/parameters/Agent"
        - $ref: "#/components/parameters/TraceId"
        - $ref: "#/components/parameters/Traceparent"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChatRequest"
            example:
              model: gpt-4o-mini
              messages:
                - role: user
                  content: Summarise our refund policy in two sentences.
      responses:
        "200":
          description: A completion from your provider, or a refusal from Bairav (check `X-Bairav-Blocked`).
          headers:
            X-Bairav-Blocked:
              description: Present with the value `1` when Bairav blocked the call and the body is a refusal.
              schema:
                type: string
                const: "1"
          content:
            application/json:
              schema:
                type: object
                description: An OpenAI chat completion, exactly as your provider returned it, or a refusal in the same shape.
                additionalProperties: true
            text/event-stream:
              schema:
                type: string
                description: Server-sent events, when `stream` is true.
        "400":
          description: The request has no `messages` array.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GatewayError"
        "401":
          $ref: "#/components/responses/InvalidKey"
        "502":
          description: The upstream provider could not be reached.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GatewayError"
  /gw/{token}/v1/{path}:
    get:
      tags:
        - Provider passthrough
      operationId: passthroughGet
      summary: Forward a GET to the provider
      description: Forwards any other `GET` under `/v1/` to your provider as it is, for example listing models. The gateway key is checked first. Only embeddings are accounted for tokens and cost.
      security:
        - ProviderKey: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - name: path
          in: path
          required: true
          description: The provider path after `/v1/`, for example `models`.
          schema:
            type: string
      responses:
        "200":
          description: The provider's response, unchanged.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/InvalidKey"
    post:
      tags:
        - Provider passthrough
      operationId: passthroughPost
      summary: Forward a POST to the provider
      description: Forwards any other `POST` under `/v1/` to your provider as it is, for example embeddings. The gateway key is checked first. Only embeddings are accounted for tokens and cost.
      security:
        - ProviderKey: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - name: path
          in: path
          required: true
          description: The provider path after `/v1/`, for example `embeddings`.
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: The provider's response, unchanged.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/InvalidKey"
  /gw/{token}/events:
    post:
      tags:
        - Reasoning trail
      operationId: reportTraceEvents
      summary: Report execution events
      description: |
        Tell Bairav what your agent did, for applications whose steps do not all go through the chat endpoint: tool calls, database actions, policy checks and so on. Events with the same `trace_id` form one trace you can read back.

        At most **200 events** and **256 KB** per request. Invalid events are counted in `rejected`; the rest are stored.
      security: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - $ref: "#/components/parameters/Agent"
        - name: X-Bairav-User
          in: header
          description: The end user on whose behalf the agent acted. Optional.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TraceEventsRequest"
            example:
              trace_id: 4bf92f35-77b3-4da6-a3ce-929d0e0e4736
              events:
                - kind: tool_call
                  ok: true
                  duration_ms: 120
                  summary: lookup_order
                - kind: db_action
                  ok: true
                  duration_ms: 8
                  summary: SELECT on orders
                  parent_index: 0
      responses:
        "200":
          description: The events were processed.
          content:
            application/json:
              schema:
                type: object
                required:
                  - trace_id
                  - accepted
                  - rejected
                properties:
                  trace_id:
                    type: string
                  accepted:
                    type: integer
                  rejected:
                    type: integer
                    description: How many events were refused (unknown `kind`, or not an object).
        "400":
          description: The body is not valid JSON, or `trace_id` and a non-empty `events` list are missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GatewayError"
        "401":
          $ref: "#/components/responses/InvalidKey"
        "413":
          description: The payload is larger than 256 KB.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GatewayError"
  /gw/{token}/db:
    post:
      tags:
        - Reasoning trail
      operationId: reportDbActivity
      summary: Report database queries
      description: Report the SQL your application ran during a chat so Bairav can show real table access on the agent graph. At most 200 queries per request.
      security: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - $ref: "#/components/parameters/Agent"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DbActivityRequest"
      responses:
        "200":
          description: The queries were processed.
          content:
            application/json:
              schema:
                type: object
                required:
                  - accepted
                properties:
                  accepted:
                    type: integer
        "401":
          $ref: "#/components/responses/InvalidKey"
  /gw/{token}/trace/{trace_id}:
    get:
      tags:
        - Reasoning trail
      operationId: getTrace
      summary: Read a trace
      description: Returns every event of one trace in chronological order, scoped to the tenant that owns the gateway key. At most 500 events. The row shape matches the Portal API's `GET /api/lineage/events/traces/{trace_id}`. Use it to render your own reasoning page without a Bairav user session.
      security: []
      parameters:
        - $ref: "#/components/parameters/Token"
        - name: trace_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The trace.
          content:
            application/json:
              schema:
                type: object
                required:
                  - trace_id
                  - items
                properties:
                  trace_id:
                    type: string
                  items:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        "401":
          $ref: "#/components/responses/InvalidKey"
  /gw/health:
    get:
      tags:
        - Health
      operationId: gatewayHealth
      summary: Check the gateway
      description: Unauthenticated liveness check.
      security: []
      responses:
        "200":
          description: The gateway is up.
          content:
            application/json:
              schema:
                type: object
                required:
                  - status
                  - service
                properties:
                  status:
                    type: string
                    const: ok
                  service:
                    type: string
                    const: ai-gateway
components:
  securitySchemes:
    ProviderKey:
      type: http
      scheme: bearer
      description: Your own model provider key. It is forwarded to the provider and never stored by Bairav. The Bairav gateway key itself is the `{token}` path segment.
  parameters:
    Token:
      name: token
      in: path
      required: true
      description: Your gateway key, `bst_<8 hex>.<secret>`. It is a secret.
      schema:
        type: string
    Agent:
      name: X-Bairav-Agent
      in: header
      description: The name of the calling agent, at most 128 characters. The first call registers the agent in your Registry. Defaults to `OpenAI Agent`.
      schema:
        type: string
        maxLength: 128
    TraceId:
      name: X-Bairav-Trace-Id
      in: header
      description: A trace UUID, to group several calls into one trace.
      schema:
        type: string
    Traceparent:
      name: traceparent
      in: header
      description: A W3C trace context header. Accepted as an alternative to `X-Bairav-Trace-Id`.
      schema:
        type: string
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: Used by cost accounting so a retried call is not counted twice. `X-Bairav-Idempotency-Key` is accepted as well.
      schema:
        type: string
  responses:
    InvalidKey:
      description: The gateway key is invalid or revoked.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/GatewayError"
          example:
            error:
              message: Invalid or revoked Bairav gateway key
              type: invalid_request_error
  schemas:
    GatewayError:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            type:
              type: string
    ChatRequest:
      type: object
      required:
        - model
        - messages
      description: An OpenAI chat-completions request. Other OpenAI fields are passed through unchanged.
      properties:
        model:
          type: string
        messages:
          type: array
          items:
            type: object
            required:
              - role
              - content
            properties:
              role:
                type: string
              content: {}
        stream:
          type: boolean
          default: false
      additionalProperties: true
    TraceEventsRequest:
      type: object
      required:
        - trace_id
        - events
      properties:
        trace_id:
          type: string
        events:
          type: array
          maxItems: 200
          items:
            type: object
            required:
              - kind
            properties:
              kind:
                type: string
                enum:
                  - llm_call
                  - tool_call
                  - db_action
                  - policy_evaluation
                  - authorization_decision
                  - egress
                  - guardrail_block
                  - observation
              ok:
                type: boolean
              duration_ms:
                type: integer
              summary:
                type: string
              detail:
                type: object
                additionalProperties: true
              parent_index:
                type: integer
                description: The position, in this array, of the event that caused this one.
    DbActivityRequest:
      type: object
      required:
        - queries
      properties:
        queries:
          type: array
          maxItems: 200
          items:
            type: object
            properties:
              operation:
                type: string
              table:
                type: string
              sql:
                type: string
              duration_ms:
                type: integer
              rows:
                type: integer
              ok:
                type: boolean
              trace_id:
                type: string
