{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://api.klarava.app/v1/schema",
  "title": "klarava API Response v1",
  "$comment": "Leading source is docs/openapi.yaml (OpenAPI 3.1 = JSON Schema 2020-12). This file should be generated from it, not maintained in parallel (concept §10).",
  "description": "Response schema of the klarava GEO/AEO API. The 'ai' block is only present on /v1/analyze and /v1/full; /v1/audit does not return it.",
  "type": "object",
  "required": [
    "schema_version",
    "generated_at",
    "url",
    "final_url",
    "requested_lang",
    "output_lang",
    "pages_checked",
    "status",
    "coverage",
    "confidence",
    "redirected",
    "geo_score",
    "brand_clarity",
    "signals",
    "findings"
  ],
  "additionalProperties": false,
  "properties": {
    "schema_version": { "type": "string", "const": "1.0" },
    "generated_at": { "type": "string", "format": "date-time" },
    "url": { "type": "string", "format": "uri", "description": "The requested URL." },
    "final_url": { "type": "string", "format": "uri", "description": "URL after redirects." },
    "requested_lang": { "type": "string", "enum": ["de", "en", "auto"], "description": "Output language requested by the call (echo)." },
    "output_lang": { "type": "string", "enum": ["de", "en"], "description": "Actual language of the text in this response (for 'auto', resolved from detected_language)." },
    "detected_language": { "type": "string", "description": "Language detected from the website (ISO-639-1, e.g. 'en')." },
    "pages_checked": { "type": "integer", "minimum": 0 },
    "status": { "type": "string", "enum": ["ok", "partial"], "description": "Crawl outcome: 'ok' = homepage analyzable, 'partial' = some planned subpages were unreachable." },
    "coverage": {
      "type": "object",
      "description": "How much of the site was covered.",
      "required": ["checked", "sitemap_total"],
      "additionalProperties": false,
      "properties": {
        "checked": { "type": "integer", "minimum": 0, "description": "Number of pages actually crawled." },
        "sitemap_total": { "type": "integer", "minimum": 0, "description": "Number of URLs found in the sitemap (0 if none)." }
      }
    },
    "confidence": { "type": "string", "enum": ["ok", "low"], "description": "'low' when content was barely readable (JS/SPA) and the assessment may be too strict." },
    "redirected": {
      "type": ["object", "null"],
      "description": "Set when the entered host differs from the final host (e.g. apex -> www), else null.",
      "required": ["from", "to"],
      "additionalProperties": false,
      "properties": {
        "from": { "type": "string", "description": "Entered host." },
        "to": { "type": "string", "description": "Final host after redirects." }
      }
    },
    "cached": { "type": "boolean", "description": "true if the response came from cache." },
    "geo_score": { "$ref": "#/$defs/geoScore" },
    "brand_clarity": { "$ref": "#/$defs/brandClarity" },
    "signals": { "$ref": "#/$defs/signals" },
    "findings": { "type": "array", "items": { "$ref": "#/$defs/finding" } },
    "fixes": {
      "type": "array",
      "description": "Ready-to-paste fix snippets derived deterministically from the facts (robots.txt, JSON-LD Organization, FAQPage scaffold). Only what is actually missing.",
      "items": {
        "type": "object",
        "required": ["id", "title", "code"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "description": "Fix kind: robots | org | person | faq." },
          "title": { "type": "string", "description": "Localized short title (output_lang)." },
          "code": { "type": "string", "description": "The snippet to paste (robots.txt block or JSON-LD <script>)." }
        }
      }
    },
    "ai": { "$ref": "#/$defs/ai" }
  },
  "$defs": {
    "severity": { "type": "string", "enum": ["high", "medium", "low"] },
    "tier": { "type": "string", "enum": ["high", "mid", "low"] },
    "coverage": { "type": "string", "enum": ["yes", "partial", "no"] },
    "score": { "type": "integer", "minimum": 0, "maximum": 100 },
    "geoScore": {
      "type": "object",
      "required": ["value", "method", "max"],
      "additionalProperties": false,
      "properties": {
        "value": { "$ref": "#/$defs/score" },
        "method": { "type": "string", "enum": ["deterministic", "llm"] },
        "max": { "type": "integer", "const": 100 }
      }
    },
    "brandClarity": {
      "type": "object",
      "description": "Second deterministic score layer: can a complete, sourced, current brand picture be built?",
      "required": ["value", "dimensions", "findings"],
      "additionalProperties": false,
      "properties": {
        "value": { "$ref": "#/$defs/score" },
        "dimensions": {
          "type": "object",
          "required": ["facts", "sources", "people", "proof", "freshness"],
          "additionalProperties": false,
          "properties": {
            "facts": { "$ref": "#/$defs/score" },
            "sources": { "$ref": "#/$defs/score" },
            "people": { "$ref": "#/$defs/score" },
            "proof": { "$ref": "#/$defs/score" },
            "freshness": { "$ref": "#/$defs/score" }
          }
        },
        "findings": {
          "type": "array",
          "description": "Per weak dimension: what is structurally missing + a concrete fix.",
          "items": {
            "type": "object",
            "required": ["dimension", "score", "severity", "missing", "label", "fix"],
            "additionalProperties": false,
            "properties": {
              "dimension": { "type": "string", "enum": ["facts", "sources", "people", "proof", "freshness"] },
              "score": { "$ref": "#/$defs/score" },
              "severity": { "$ref": "#/$defs/severity" },
              "missing": { "type": "array", "items": { "type": "string" } },
              "label": { "type": "string" },
              "fix": { "type": "string" }
            }
          }
        }
      }
    },
    "signals": {
      "type": "object",
      "description": "Raw, deterministic crawl facts (no AI).",
      "additionalProperties": false,
      "properties": {
        "ai_access": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "blocked": { "type": "array", "items": { "type": "string" }, "description": "AI crawlers blocked in robots.txt." },
            "allowed": { "type": "boolean" }
          }
        },
        "indexable": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "noindex": { "type": "integer", "minimum": 0 },
            "total": { "type": "integer", "minimum": 0 }
          }
        },
        "schema_types": { "type": "array", "items": { "type": "string" } },
        "schema_depth": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "organization": { "type": "boolean" },
            "logo": { "type": "boolean" },
            "address": { "type": "boolean" },
            "contact_point": { "type": "boolean" },
            "breadcrumb": { "type": "boolean" },
            "website_search": { "type": "boolean" },
            "article_date": { "type": "boolean" },
            "aggregate_rating": {
              "type": ["object", "null"],
              "description": "Recognized AggregateRating values from schema, or null if absent.",
              "additionalProperties": false,
              "properties": {
                "rating_value": { "type": ["number", "null"] },
                "review_count": { "type": ["number", "null"] }
              }
            }
          }
        },
        "headings": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "one_h1": { "type": "boolean" },
            "hierarchy_ok": { "type": "boolean" }
          }
        },
        "head": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "canonical": { "type": "boolean" },
            "open_graph": { "type": "boolean" },
            "twitter_card": { "type": "boolean" }
          }
        },
        "authority": { "type": "array", "items": { "type": "string" }, "description": "Linked external authority/entity profiles (sameAs, etc.)." },
        "semantics": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "landmarks": { "type": "boolean" },
            "imgs": { "type": "integer", "minimum": 0 },
            "with_alt": { "type": "integer", "minimum": 0 }
          }
        },
        "content": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "thin_pages": { "type": "integer", "minimum": 0 },
            "total": { "type": "integer", "minimum": 0 },
            "min_words": { "type": "integer", "minimum": 0 }
          }
        },
        "sitemap_xml": { "type": "boolean" },
        "robots_txt": { "type": "boolean" },
        "llms_txt": { "type": "boolean" },
        "contact": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "email": { "type": "boolean" },
            "phone": { "type": "boolean" },
            "address": { "type": "boolean" }
          }
        },
        "meta_coverage": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "with_title": { "type": "integer", "minimum": 0 },
            "with_meta": { "type": "integer", "minimum": 0 },
            "total": { "type": "integer", "minimum": 0 }
          }
        },
        "ai_directives": {
          "type": "object",
          "description": "May an AI use/cite the content? (meta robots + X-Robots-Tag + TDMRep)",
          "additionalProperties": false,
          "properties": {
            "noai": { "type": "boolean" },
            "noimageai": { "type": "boolean" },
            "nosnippet": { "type": "boolean" },
            "max_snippet": { "type": ["integer", "null"] },
            "tdm_reservation": { "type": "boolean" },
            "restricted": { "type": "boolean" }
          }
        },
        "freshness": {
          "type": "object",
          "description": "Recency/date signals (AI prefers fresh, verifiable sources).",
          "additionalProperties": false,
          "properties": {
            "date_published": { "type": ["string", "null"] },
            "date_modified": { "type": ["string", "null"] },
            "sitemap_lastmod": { "type": ["string", "null"] },
            "freshness_days": { "type": ["integer", "null"], "minimum": 0 },
            "has_visible_updated_date": { "type": "boolean" }
          }
        },
        "answer_structure": {
          "type": "object",
          "description": "Is the content usable as an AI answer? (AEO)",
          "additionalProperties": false,
          "properties": {
            "question_headings": { "type": "integer", "minimum": 0 },
            "tables_with_headers": { "type": "integer", "minimum": 0 },
            "definition_lists": { "type": "integer", "minimum": 0 },
            "list_items": { "type": "integer", "minimum": 0 },
            "has_summary": { "type": "boolean" },
            "answerable": { "type": "boolean" }
          }
        },
        "language": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "html_lang": { "type": "string" },
            "hreflang_count": { "type": "integer", "minimum": 0 },
            "has_x_default": { "type": "boolean" },
            "multilingual": { "type": "boolean" },
            "consistency": { "type": "boolean" }
          }
        },
        "rendering": {
          "type": "object",
          "description": "Is the content in the HTML (SSR) or only via JavaScript (SPA)?",
          "additionalProperties": false,
          "properties": {
            "spa_like": { "type": "boolean" },
            "home_text_length": { "type": "integer", "minimum": 0 }
          }
        },
        "source_mix": {
          "type": "object",
          "description": "Source mix (Brand Clarity input): owned/social/authority/people/evidence sources.",
          "additionalProperties": false,
          "properties": {
            "owned_sources": { "type": "array", "items": { "type": "string" } },
            "social_profiles": { "type": "array", "items": { "type": "string" } },
            "authority_profiles": { "type": "array", "items": { "type": "string" } },
            "people_profiles": { "type": "array", "items": { "type": "string" } },
            "evidence_sources": { "type": "array", "items": { "type": "string" } },
            "source_diversity": { "type": "integer", "minimum": 0, "maximum": 100 }
          }
        },
        "entity_completeness": {
          "type": "object",
          "description": "Structural entity completeness (Brand Clarity input, facts dimension).",
          "additionalProperties": false,
          "properties": {
            "organization_schema": { "type": "boolean" },
            "name": { "type": "boolean" },
            "logo": { "type": "boolean" },
            "address": { "type": "boolean" },
            "contact_point": { "type": "boolean" },
            "founding_date": { "type": "boolean" },
            "management_page": { "type": "boolean" },
            "press_page": { "type": "boolean" },
            "impressum": { "type": "boolean" },
            "score": { "type": "integer", "minimum": 0, "maximum": 100 }
          }
        },
        "people_signals": {
          "type": "object",
          "description": "People/management anchors (Brand Clarity input, people dimension). named_people only from Person schema.",
          "additionalProperties": false,
          "properties": {
            "team_page": { "type": "boolean" },
            "named_people": { "type": "integer", "minimum": 0 },
            "roles_visible": { "type": "boolean" },
            "linkedin_profiles": { "type": "integer", "minimum": 0 },
            "spokesperson_contact": { "type": "boolean" },
            "score": { "type": "integer", "minimum": 0, "maximum": 100 }
          }
        }
      }
    },
    "finding": {
      "type": "object",
      "required": ["id", "tier", "severity", "label"],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "Stable finding ID (contract). Set defined in geoscore.mjs.",
          "enum": [
            "ai_access", "indexable", "schema_org", "authority", "sitemap",
            "canonical", "one_h1", "meta_desc", "hierarchy", "contact",
            "faq_schema", "content", "open_graph", "semantics", "img_alt", "llms",
            "ai_directives", "ssr", "answerable"
          ]
        },
        "tier": { "$ref": "#/$defs/tier" },
        "weight": { "type": "integer", "minimum": 0 },
        "severity": { "$ref": "#/$defs/severity" },
        "label": { "type": "string", "description": "Display text (not stable, language-dependent)." }
      }
    },
    "dimension": {
      "type": "object",
      "required": ["score", "evidence"],
      "additionalProperties": false,
      "properties": {
        "score": { "$ref": "#/$defs/score" },
        "evidence": { "type": "string" }
      }
    },
    "ai": {
      "type": "object",
      "description": "LLM analysis (only /v1/analyze and /v1/full).",
      "required": ["overall", "dimensions", "gaps", "query_fanout", "ai_summary"],
      "additionalProperties": false,
      "properties": {
        "overall": { "$ref": "#/$defs/score" },
        "dimensions": {
          "type": "object",
          "required": ["identity", "offering", "audience", "differentiation", "proof", "structure"],
          "additionalProperties": false,
          "properties": {
            "identity": { "$ref": "#/$defs/dimension" },
            "offering": { "$ref": "#/$defs/dimension" },
            "audience": { "$ref": "#/$defs/dimension" },
            "differentiation": { "$ref": "#/$defs/dimension" },
            "proof": { "$ref": "#/$defs/dimension" },
            "structure": { "$ref": "#/$defs/dimension" }
          }
        },
        "gaps": {
          "type": "array",
          "maxItems": 5,
          "items": {
            "type": "object",
            "required": ["title", "why", "fix", "severity"],
            "additionalProperties": false,
            "properties": {
              "title": { "type": "string" },
              "evidence": { "type": "string", "description": "Verbatim quote from this site (or precise location of what is missing) grounding the gap." },
              "why": { "type": "string" },
              "fix": { "type": "string" },
              "severity": { "$ref": "#/$defs/severity" }
            }
          }
        },
        "query_fanout": {
          "type": "array",
          "maxItems": 5,
          "items": {
            "type": "object",
            "required": ["query", "coverage", "evidence"],
            "additionalProperties": false,
            "properties": {
              "query": { "type": "string" },
              "coverage": { "$ref": "#/$defs/coverage" },
              "evidence": { "type": "string" }
            }
          }
        },
        "sample_faq": { "type": "string" },
        "target_summary": { "type": "string" },
        "ai_summary": { "type": "string" }
      }
    }
  }
}
