{
  "openapi": "3.1.0",
  "info": {
    "title": "Ginjo Gateway API",
    "version": "0.1.0",
    "summary": "Policy-aware, OpenAI-compatible model gateway",
    "description": "Ginjo authenticates, applies application policy, screens, routes, and traces OpenAI-compatible requests before model-provider egress. This document is the curated public developer contract; control-plane and administrative routes are intentionally excluded."
  },
  "servers": [
    {
      "url": "https://www.ginjo.ai",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Models",
      "description": "Discover policy-visible model aliases and provider models."
    },
    {
      "name": "Inference",
      "description": "OpenAI-compatible generation endpoints protected by Ginjo policy."
    },
    {
      "name": "Operations",
      "description": "Public deployment readiness."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "List allowed models",
        "description": "Returns Ginjo aliases and upstream models visible under the policy attached to the authenticated API key.",
        "operationId": "listModels",
        "responses": {
          "200": {
            "description": "Policy-visible models",
            "headers": {
              "x-ginjo-trace-id": {
                "$ref": "#/components/headers/XGinjoTraceId"
              },
              "x-ginjo-policy-id": {
                "$ref": "#/components/headers/XGinjoPolicyId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/chat/completions": {
      "post": {
        "tags": [
          "Inference"
        ],
        "summary": "Create a chat completion",
        "description": "Accepts an OpenAI-compatible Chat Completions request. When stream is true, the response is forwarded as server-sent events.",
        "operationId": "createChatCompletion",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              },
              "examples": {
                "basic": {
                  "summary": "Basic completion",
                  "value": {
                    "model": "auto",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Explain least privilege in two sentences."
                      }
                    ]
                  }
                },
                "streaming": {
                  "summary": "Stream completion events",
                  "value": {
                    "model": "auto",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Give me three launch checks."
                      }
                    ],
                    "stream": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/AdmittedModelResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PolicyRejected"
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/TimedOut"
          }
        }
      }
    },
    "/v1/responses": {
      "post": {
        "tags": [
          "Inference"
        ],
        "summary": "Create a response",
        "description": "Accepts an OpenAI-compatible Responses request, including built-in or function tools permitted by application policy. When stream is true, the response is forwarded as server-sent events.",
        "operationId": "createResponse",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResponsesRequest"
              },
              "examples": {
                "basic": {
                  "summary": "Basic response",
                  "value": {
                    "model": "auto",
                    "input": "Write a safe rollout checklist."
                  }
                },
                "streaming": {
                  "summary": "Stream response events",
                  "value": {
                    "model": "auto",
                    "input": "Give me three launch checks.",
                    "stream": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/AdmittedModelResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PolicyRejected"
          },
          "413": {
            "$ref": "#/components/responses/RequestTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "$ref": "#/components/responses/TimedOut"
          }
        }
      }
    },
    "/health/ready": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "Read deployment readiness",
        "description": "Returns 200 when all dependencies required by the configured fail mode are ready, otherwise 503.",
        "operationId": "getReadiness",
        "security": [],
        "responses": {
          "200": {
            "description": "Deployment is ready",
            "headers": {
              "x-ginjo-trace-id": {
                "$ref": "#/components/headers/XGinjoTraceId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          },
          "503": {
            "description": "A required dependency is unavailable",
            "headers": {
              "x-ginjo-trace-id": {
                "$ref": "#/components/headers/XGinjoTraceId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "gjo_live_…",
        "description": "A revocable Ginjo application API key generated in the console. Keep it on the server."
      }
    },
    "headers": {
      "XGinjoTraceId": {
        "description": "Server-generated UUID identifying the request and its decision trace.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "XGinjoVerdict": {
        "description": "Guard verdict before enforcement-mode interpretation.",
        "schema": {
          "$ref": "#/components/schemas/Verdict"
        }
      },
      "XGinjoEnforcedVerdict": {
        "description": "Verdict actually enforced after policy mode and rewrite revalidation.",
        "schema": {
          "$ref": "#/components/schemas/Verdict"
        }
      },
      "XGinjoPolicyMode": {
        "description": "Application policy mode used for the request.",
        "schema": {
          "type": "string",
          "enum": [
            "enforce",
            "shadow"
          ]
        }
      },
      "XGinjoPolicyId": {
        "description": "UUID of the named policy attached to the authenticated API key and enforced for this request.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "XGinjoRewritten": {
        "description": "Whether Ginjo changed the effective request before provider egress.",
        "schema": {
          "type": "boolean"
        }
      },
      "XGinjoRiskScore": {
        "description": "Optional normalized guard risk score.",
        "schema": {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        }
      },
      "XGinjoSelectedModel": {
        "description": "Actual provider model selected for the admitted request.",
        "schema": {
          "type": "string"
        }
      },
      "XGinjoRoutingRationale": {
        "description": "Optional provider routing summary.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds until the current hosted quota window can accept another request.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "AdmittedModelResponse": {
        "description": "OpenAI-compatible upstream response with Ginjo decision metadata. The media type is text/event-stream when stream is true.",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          },
          "x-ginjo-verdict": {
            "$ref": "#/components/headers/XGinjoVerdict"
          },
          "x-ginjo-enforced-verdict": {
            "$ref": "#/components/headers/XGinjoEnforcedVerdict"
          },
          "x-ginjo-policy-mode": {
            "$ref": "#/components/headers/XGinjoPolicyMode"
          },
          "x-ginjo-policy-id": {
            "$ref": "#/components/headers/XGinjoPolicyId"
          },
          "x-ginjo-rewritten": {
            "$ref": "#/components/headers/XGinjoRewritten"
          },
          "x-ginjo-risk-score": {
            "$ref": "#/components/headers/XGinjoRiskScore"
          },
          "x-ginjo-selected-model": {
            "$ref": "#/components/headers/XGinjoSelectedModel"
          },
          "x-ginjo-routing-rationale": {
            "$ref": "#/components/headers/XGinjoRoutingRationale"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "text/event-stream": {
            "schema": {
              "type": "string",
              "description": "OpenAI-compatible server-sent event stream."
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed JSON or invalid request fields",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, expired, or revoked API key",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          },
          "WWW-Authenticate": {
            "description": "Authentication scheme required by the gateway.",
            "schema": {
              "type": "string",
              "const": "Bearer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PolicyRejected": {
        "description": "Application policy blocked the request or requires additional review",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": {
                "message": "Request blocked by application safety policy.",
                "type": "policy_error",
                "param": null,
                "code": "policy_blocked"
              },
              "trace_id": "1f64c2c2-e54a-43f4-b328-a5956de19576"
            }
          }
        }
      },
      "RequestTooLarge": {
        "description": "Request exceeds the deployment body-size limit",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Hosted request quota reached",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unavailable": {
        "description": "A required safety or model dependency is unavailable",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "TimedOut": {
        "description": "The model provider timed out",
        "headers": {
          "x-ginjo-trace-id": {
            "$ref": "#/components/headers/XGinjoTraceId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "Verdict": {
        "type": "string",
        "enum": [
          "PASS",
          "REWRITE",
          "BLOCK",
          "ESCALATE"
        ]
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "description": "OpenAI-compatible message role."
          },
          "content": {
            "description": "String or structured OpenAI-compatible message content.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {}
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": true
      },
      "Tool": {
        "type": "object",
        "description": "OpenAI-compatible function or built-in tool. Its policy identity is the function name, name, or non-function type.",
        "additionalProperties": true
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "Ginjo alias such as auto, efficient, or capable, or another policy-allowed model ID."
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 1
          },
          "max_completion_tokens": {
            "type": "integer",
            "minimum": 1
          },
          "tools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tool"
            }
          }
        },
        "additionalProperties": true
      },
      "ResponsesRequest": {
        "type": "object",
        "required": [
          "model",
          "input"
        ],
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "description": "Ginjo alias such as auto, efficient, or capable, or another policy-allowed model ID."
          },
          "input": {
            "description": "OpenAI-compatible Responses input, including strings or structured input items."
          },
          "instructions": {
            "type": "string"
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "max_output_tokens": {
            "type": "integer",
            "minimum": 1
          },
          "tools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tool"
            }
          }
        },
        "additionalProperties": true
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "model"
          },
          "owned_by": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        },
        "additionalProperties": true
      },
      "ErrorDetail": {
        "type": "object",
        "required": [
          "message",
          "type",
          "code"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "param": {
            "type": [
              "string",
              "null"
            ]
          },
          "code": {
            "type": "string"
          },
          "review_required": {
            "type": "boolean"
          },
          "limit_scope": {
            "type": "string"
          },
          "limit": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          },
          "trace_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "unavailable"
            ]
          },
          "version": {
            "type": "string"
          },
          "dependencies": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
