{
  "openapi": "3.1.0",
  "info": {
    "title": "LLMFence Guardrail API",
    "version": "1.0.0",
    "summary": "Checks text produced by an AI agent and reports whether it is safe to send.",
    "description": "Send the text your agent is about to emit. You get back a risk score from\n0 to 100, a verdict, a list of findings, and a redacted copy with sensitive\nspans masked.\n\nDetects personally identifiable information, prompt-injection attempts,\nhallucinated claims presented as fact, and brand-safety violations. Model\nagnostic: it evaluates text, so it does not matter what produced it.\n\nPlans differ by included volume and request size, never by analysis quality:\n- Developer: 100 calls/month, 10/min, up to 5,000 characters per request, overage not available — hard stop\n- Launch: 50,000 calls/month, 120/min, up to 50,000 characters per request, overage $0.001\n- Scale: 500,000 calls/month, 600/min, up to 200,000 characters per request, overage $0.0005",
    "contact": {
      "url": "https://llmfence.vercel.app/contact"
    },
    "termsOfService": "https://llmfence.vercel.app/terms"
  },
  "servers": [
    {
      "url": "https://llmfence.vercel.app",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "url": "https://llmfence.vercel.app/docs",
    "description": "Full documentation"
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key minted in the dashboard. Sent as `Authorization: Bearer sk_...`. Keys are stored only as a SHA-256 hash and shown once."
      }
    },
    "schemas": {
      "CheckRequest": {
        "type": "object",
        "required": [
          "proposed_text"
        ],
        "properties": {
          "proposed_text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200000,
            "description": "The text to evaluate. The maximum length depends on your plan: Developer 5,000, Launch 50,000, Scale 200,000."
          },
          "agent_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.-]{1,128}$",
            "maxLength": 128,
            "description": "Optional label for the agent making the call, stored against the request so you can see which of your agents produced a verdict. 1-128 characters of A-Z a-z 0-9 _ . -. Never used for authorisation."
          }
        }
      },
      "CheckResponse": {
        "type": "object",
        "required": [
          "risk_score",
          "status",
          "flags",
          "redacted_text"
        ],
        "properties": {
          "risk_score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "status": {
            "type": "string",
            "enum": [
              "approved",
              "flagged",
              "rejected"
            ],
            "description": "A 200 does not mean approved. Read this field."
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Short descriptions of each finding."
          },
          "redacted_text": {
            "type": "string",
            "description": "The input with sensitive spans replaced by asterisks."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/v1/guardrail/check": {
      "post": {
        "operationId": "checkText",
        "summary": "Evaluate one piece of text",
        "description": "Answered by the first stage that can decide it: a deterministic scan, a short-lived verdict cache, or full model analysis. The response header X-LLMFence-Source names which. Fails closed — an analysis that cannot complete returns a rejection, never an approval.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Evaluated.",
            "headers": {
              "X-LLMFence-Source": {
                "description": "Which stage produced the verdict.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "fastpath",
                    "cache",
                    "model"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "description": "Body is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key missing, invalid or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Included calls exhausted on a plan that stops (Developer), or the monthly spend cap was reached. The key is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Account suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Text exceeds the plan's maximum request size.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "proposed_text missing or empty, or agent_id malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The plan's per-minute ceiling. Not counted and not charged.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Authorisation could not complete. Fails closed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}