{
  "openapi": "3.1.0",
  "info": {
    "title": "Certiv Product API",
    "summary": "Authenticated product API for the Certiv AI agent governance platform.",
    "description": "Machine-readable OpenAPI 3.1 description of the Certiv product API served from the app host (app.certiv.ai) and hosted at https://api.certiv.ai. Certiv is an enterprise runtime-assurance platform for AI agents: it discovers agents, captures reasoning and tool calls, and enforces policy before risky actions execute.\n\nThis is the authenticated, account-scoped product API. Most endpoints require a bearer token issued to a Certiv organization account (see the bearerAuth security scheme and https://app.certiv.ai/auth.md). A small number of endpoints (health, token issuance, public counters) are unauthenticated and are marked with an empty security requirement. For the public lead-capture surface (request a demo, join the waitlist) see the separate spec at https://certiv.ai/openapi.json.\n\nTenant isolation: every authenticated request is scoped to the caller's organization. A token for one organization cannot read or act on another organization's data; isolation is enforced server-side.\n\nVersioning and deprecation: breaking changes ship under a new major version. Additive, non-breaking changes may ship without a version bump. Deprecated behavior is announced at least 90 days before removal via the Deprecation and Sunset response headers (RFC 8594). For the authoritative, always-current endpoint reference see https://docs.certiv.ai.",
    "version": "1.0.0",
    "termsOfService": "https://certiv.ai/terms/",
    "contact": {
      "name": "Certiv",
      "url": "https://certiv.ai/",
      "email": "hello@certiv.ai"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://certiv.ai/terms/"
    }
  },
  "servers": [
    {
      "url": "https://api.certiv.ai",
      "description": "Production product API"
    }
  ],
  "externalDocs": {
    "description": "Certiv product API and authentication documentation",
    "url": "https://docs.certiv.ai"
  },
  "tags": [
    { "name": "System", "description": "Health and readiness endpoints (unauthenticated)." },
    { "name": "Authentication", "description": "Obtain and refresh bearer tokens." },
    { "name": "Policies", "description": "Governance policy catalog (bearer auth)." },
    { "name": "Capabilities", "description": "Agent and tool capability records (bearer auth)." },
    { "name": "Public counters", "description": "Public aggregate counters (unauthenticated)." }
  ],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getHealth",
        "tags": ["System"],
        "summary": "Health check",
        "description": "Liveness and readiness probe for the product API. Unauthenticated. Returns 200 when the service is healthy.",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is healthy.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthResponse" },
                "examples": { "healthy": { "value": { "status": "ok" } } }
              }
            }
          }
        }
      }
    },
    "/auth/login": {
      "post": {
        "operationId": "login",
        "tags": ["Authentication"],
        "summary": "Obtain a bearer token",
        "description": "Exchange account credentials for a bearer access token and refresh token. Unauthenticated. Present the returned access token in the Authorization header on subsequent product API calls.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authentication succeeded.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TokenResponse" }
              }
            }
          },
          "401": {
            "description": "Invalid credentials.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/auth/refresh": {
      "post": {
        "operationId": "refreshToken",
        "tags": ["Authentication"],
        "summary": "Refresh a bearer token",
        "description": "Exchange a valid refresh token for a new access token. Unauthenticated at the header level; the refresh token is supplied in the request body.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RefreshRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New access token issued.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TokenResponse" }
              }
            }
          },
          "401": {
            "description": "Refresh token is missing, invalid, or expired.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/policies": {
      "get": {
        "operationId": "listPolicies",
        "tags": ["Policies"],
        "summary": "List governance policies",
        "description": "List policies in the caller's organization. Results are scoped to the caller's organization; another organization's policies are never returned. Requires a bearer token.",
        "responses": {
          "200": {
            "description": "Policies for the caller's organization.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Policy" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "post": {
        "operationId": "createPolicy",
        "tags": ["Policies"],
        "summary": "Create a governance policy",
        "description": "Create a policy in the caller's organization. Requires a bearer token.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Policy" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Policy created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Policy" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/policies/{policy_id}": {
      "get": {
        "operationId": "getPolicy",
        "tags": ["Policies"],
        "summary": "Get a governance policy",
        "description": "Fetch a single policy by id from the caller's organization. Requires a bearer token.",
        "parameters": [
          {
            "name": "policy_id",
            "in": "path",
            "required": true,
            "description": "Policy identifier.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested policy.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Policy" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "No such policy in the caller's organization.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/capabilities": {
      "get": {
        "operationId": "listCapabilities",
        "tags": ["Capabilities"],
        "summary": "List capabilities",
        "description": "List agent and tool capability records for the caller's organization. Requires a bearer token.",
        "responses": {
          "200": {
            "description": "Capability records for the caller's organization.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Capability" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/waitlist/count": {
      "get": {
        "operationId": "getWaitlistCount",
        "tags": ["Public counters"],
        "summary": "Waitlist count",
        "description": "Public aggregate count of waitlist signups. Unauthenticated.",
        "security": [],
        "responses": {
          "200": {
            "description": "Aggregate count.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CountResponse" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Organization-scoped bearer token. Obtain a token via POST /auth/login or an organization API key from https://app.certiv.ai, then send it as `Authorization: Bearer <token>` over TLS on every authenticated request. A token only grants access to the organization it was issued for. See https://app.certiv.ai/auth.md."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid, or expired bearer token.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": { "missingToken": { "value": { "error": "unauthorized" } } }
          }
        }
      }
    },
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": {
            "type": "string",
            "description": "Health status.",
            "examples": ["ok"]
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": ["email", "password"],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Account email address.",
            "examples": ["agent-operator@company.com"]
          },
          "password": {
            "type": "string",
            "format": "password",
            "description": "Account password."
          }
        }
      },
      "RefreshRequest": {
        "type": "object",
        "required": ["refresh_token"],
        "properties": {
          "refresh_token": {
            "type": "string",
            "description": "A valid refresh token previously issued by /auth/login."
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": ["access_token"],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Bearer access token to send as `Authorization: Bearer <token>`."
          },
          "refresh_token": {
            "type": "string",
            "description": "Refresh token used to obtain a new access token via /auth/refresh."
          },
          "token_type": {
            "type": "string",
            "description": "Token type.",
            "examples": ["Bearer"]
          },
          "expires_in": {
            "type": "integer",
            "description": "Access token lifetime in seconds.",
            "examples": [3600]
          }
        }
      },
      "Policy": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "id": {
            "type": "string",
            "description": "Policy identifier."
          },
          "name": {
            "type": "string",
            "description": "Human-readable policy name.",
            "examples": ["Block risky shell commands"]
          },
          "description": {
            "type": "string",
            "description": "What the policy governs."
          },
          "type": {
            "type": "string",
            "description": "Policy evaluation type.",
            "enum": ["deterministic", "intent"],
            "examples": ["intent"]
          },
          "organization_id": {
            "type": "string",
            "description": "Owning organization. Read-only; set from the caller's token and never cross-organization."
          }
        }
      },
      "Capability": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "id": { "type": "string", "description": "Capability identifier." },
          "name": { "type": "string", "description": "Capability name." },
          "description": { "type": "string", "description": "What the capability does." }
        }
      },
      "CountResponse": {
        "type": "object",
        "required": ["count"],
        "properties": {
          "count": {
            "type": "integer",
            "description": "Aggregate count.",
            "examples": [1234]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message.",
            "examples": ["unauthorized", "not found"]
          }
        }
      }
    }
  }
}
