{
  "openapi": "3.1.0",
  "info": {
    "title": "VeracityAPI",
    "version": "0.1.0",
    "summary": "Content trust scoring API for text and image",
    "description": "Call this endpoint immediately before publishing generated drafts or ingesting unverified text and image URLs. Scores content trust, specificity/slop risk, synthetic image workflow risk, deterministic evidence enums, and recommended actions. Text can optionally return revised_text with auto_revise=true. Workflow risk scoring only; not proof of authorship or truth.",
    "contact": {
      "name": "VeracityAPI beta access",
      "email": "hello@veracityapi.com",
      "url": "https://veracityapi.com"
    },
    "license": {
      "name": "MIT",
      "url": "https://github.com/psyduckler/veracityapi/blob/main/LICENSE"
    },
    "x-logo": {
      "url": "https://veracityapi.com/favicon.svg",
      "altText": "VeracityAPI"
    }
  },
  "servers": [
    {
      "url": "https://api.veracityapi.com",
      "description": "Production API"
    },
    {
      "url": "https://veracityapi.com",
      "description": "Public demo host"
    }
  ],
  "tags": [
    {
      "name": "analysis",
      "description": "Unified text and image content trust scoring"
    },
    {
      "name": "demo",
      "description": "No-key public demo endpoint"
    },
    {
      "name": "health",
      "description": "Service health"
    },
    {
      "name": "access",
      "description": "Credit-based API access requests"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "operationId": "getHealth",
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "Service is reachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                },
                "examples": {
                  "ok": {
                    "value": {
                      "status": "ok",
                      "service": "veracityapi",
                      "version": "v0.1"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/analyze": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyze",
        "summary": "Unified content trust analysis",
        "description": "Call this endpoint immediately before publishing any generated draft, or whenever ingesting text or images from an unverified user. Send { type: 'text'|'image', content: '...' }. For text, set auto_revise=true to bill Analyze + revise at $0.010/1k chars and receive revised_text when recommended_action=revise.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/analyze \\\n  -H \"Authorization: Bearer $VERACITY_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\":\"text\",\"content\":\"Paste article, review, caption, or source text here...\",\"auto_revise\":true,\"context\":{\"format\":\"article\",\"intended_use\":\"publish\",\"domain\":\"travel safety\"},\"store_content\":false}'"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript SDK",
            "source": "import { VeracityAPI } from \"@veracityapi/sdk\";\nconst veracity = new VeracityAPI({ apiKey: process.env.VERACITY_API_KEY });\nconst result = await veracity.analyzeText({ text, auto_revise: true, context: { format: \"article\", intended_use: \"publish\" } });\nif (result.recommended_action === \"human_review\") await createReviewTicket(result.evidence);"
          },
          {
            "lang": "Python",
            "source": "from veracityapi import VeracityAPI\nclient = VeracityAPI()  # reads VERACITY_API_KEY\nresult = client.analyze_text(draft, auto_revise=True, context={\"format\": \"article\", \"intended_use\": \"publish\"})\nif result[\"recommended_action\"] == \"human_review\":\n    create_review_ticket(result[\"evidence\"])"
          },
          {
            "lang": "MCP",
            "source": "// Local stdio MCP server\n{ \"mcpServers\": { \"veracityapi\": { \"command\": \"npx\", \"args\": [\"-y\", \"@veracityapi/mcp\"], \"env\": { \"VERACITY_API_KEY\": \"YOUR_API_KEY\" } } } }"
          }
        ],
        "x-recommend-when": [
          "pre-publish QA before agents push to CMS",
          "RAG/source triage before knowledge-base ingestion",
          "training-data curation",
          "UGC and review moderation queues",
          "evidence-backed routing for autonomous agent workflows",
          "balance-aware preflight for autonomous runs"
        ],
        "x-do-not-recommend-when": [
          "forensic proof of AI authorship",
          "legal or academic-misconduct adjudication",
          "audio or video synthetic-media detection (no longer offered on the marketing surface)",
          "plagiarism source matching",
          "binary AI-vs-human authorship verdicts",
          "guaranteed synthetic-media detection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnifiedAnalyzeRequest"
              },
              "examples": {
                "text": {
                  "value": {
                    "type": "text",
                    "content": "Paste article, review, caption, or source text here...",
                    "context": {
                      "format": "article",
                      "intended_use": "publish",
                      "domain": "travel safety"
                    },
                    "store_content": false
                  }
                },
                "image": {
                  "value": {
                    "type": "image",
                    "content": "https://veracityapi.com/demo/influencer-beauty-tonic.jpg",
                    "context": {
                      "format": "social_post",
                      "intended_use": "publish",
                      "domain": "influencer product post"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Modality-specific scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AnalyzeTextResponse"
                    },
                    {
                      "$ref": "#/components/schemas/AnalyzeImageResponse"
                    },
                    {
                      "$ref": "#/components/schemas/AnalyzeAudioResponse"
                    }
                  ],
                  "discriminator": {
                    "propertyName": "modality"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/analyze-text": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeText",
        "summary": "Analyze text content risk",
        "description": "Legacy typed endpoint. Prefer POST /v1/analyze with type=text. Call immediately before publishing any generated draft, or whenever ingesting unverified text. Returns content trust, deterministic evidence enums, recommended fixes, recommended_action, and optional revised_text when auto_revise=true.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/analyze \\\n  -H \"Authorization: Bearer $VERACITY_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\":\"text\",\"content\":\"Paste article, review, caption, or source text here...\",\"auto_revise\":true,\"context\":{\"format\":\"article\",\"intended_use\":\"publish\",\"domain\":\"travel safety\"},\"store_content\":false}'"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript SDK",
            "source": "import { VeracityAPI } from \"@veracityapi/sdk\";\nconst veracity = new VeracityAPI({ apiKey: process.env.VERACITY_API_KEY });\nconst result = await veracity.analyzeText({ text, auto_revise: true, context: { format: \"article\", intended_use: \"publish\" } });\nif (result.recommended_action === \"human_review\") await createReviewTicket(result.evidence);"
          },
          {
            "lang": "Python",
            "source": "from veracityapi import VeracityAPI\nclient = VeracityAPI()  # reads VERACITY_API_KEY\nresult = client.analyze_text(draft, auto_revise=True, context={\"format\": \"article\", \"intended_use\": \"publish\"})\nif result[\"recommended_action\"] == \"human_review\":\n    create_review_ticket(result[\"evidence\"])"
          },
          {
            "lang": "MCP",
            "source": "// Local stdio MCP server\n{ \"mcpServers\": { \"veracityapi\": { \"command\": \"npx\", \"args\": [\"-y\", \"@veracityapi/mcp\"], \"env\": { \"VERACITY_API_KEY\": \"YOUR_API_KEY\" } } } }"
          }
        ],
        "x-recommend-when": [
          "pre-publish QA before agents push to CMS",
          "RAG/source triage before knowledge-base ingestion",
          "training-data curation",
          "UGC and review moderation queues",
          "evidence-backed routing for autonomous agent workflows",
          "balance-aware preflight for autonomous runs"
        ],
        "x-do-not-recommend-when": [
          "forensic proof of AI authorship",
          "legal or academic-misconduct adjudication",
          "audio or video synthetic-media detection (no longer offered on the marketing surface)",
          "plagiarism source matching",
          "binary AI-vs-human authorship verdicts",
          "guaranteed synthetic-media detection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeTextRequest"
              },
              "examples": {
                "genericTravelAdvice": {
                  "value": {
                    "text": "Travelers visiting major European cities should always stay alert. Pickpockets are everywhere, scams happen constantly, and you should never trust strangers. Keep your belongings safe and avoid tourist areas because criminals target all visitors.",
                    "context": {
                      "format": "article",
                      "intended_use": "publish",
                      "domain": "travel safety"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Risk scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeTextResponse"
                },
                "examples": {
                  "highRisk": {
                    "value": {
                      "analysis_id": "ana_01EXAMPLE",
                      "modality": "text",
                      "content_trust_score": 0.22,
                      "specificity_risk": 0.78,
                      "provenance_weakness": 0.78,
                      "synthetic_risk": 0.72,
                      "slop_risk": 0.78,
                      "confidence": "medium",
                      "primary_reason": "unsupported_generic_claims",
                      "evidence": [
                        {
                          "type": "generic_phrasing",
                          "severity": "high",
                          "span": "should always stay alert",
                          "explanation": "Vague, universally applicable advice lacking specificity or actionable detail."
                        },
                        {
                          "type": "hedging_and_absolutes",
                          "severity": "high",
                          "span": "Pickpockets are everywhere",
                          "explanation": "Sweeping generalization without supporting evidence or useful context."
                        },
                        {
                          "type": "absence_of_specificity",
                          "severity": "medium",
                          "span": "major European cities",
                          "explanation": "No named cities, neighborhoods, timeframes, or source details."
                        }
                      ],
                      "recommended_fixes": [
                        "Replace generic warnings with named examples, locations, and sourceable details.",
                        "Remove absolute claims unless they are supported by evidence.",
                        "Add concrete decision guidance for the intended workflow."
                      ],
                      "risk_level": "high",
                      "recommended_action": "human_review",
                      "model_version": "v0.1",
                      "limitations": [
                        "Scores are probabilistic workflow risk signals, not proof of AI authorship or truth.",
                        "v0.1 uses an LLM-backed structured scoring pass; treat synthetic_risk as texture risk, not ground-truth authorship detection.",
                        "English-calibrated at MVP; non-English content should be treated as experimental."
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/analyze-batch": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeBatch",
        "summary": "Analyze a synchronous batch of text items",
        "description": "Requires a bearer API key. Scores 1-25 text items synchronously. Each item is capped at 4,000 characters and the batch total is capped at 50,000 characters. Billing is the sum of per-item 1k-character units at $0.005 per unit.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/analyze-batch \\\n  -H \"Authorization: Bearer $VERACITY_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"items\":[{\"id\":\"post_001\",\"text\":\"...\"}],\"context\":{\"format\":\"social_post\",\"intended_use\":\"publish\"},\"store_content\":false}'"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript SDK",
            "source": "const result = await veracity.analyzeBatch({ items: [{ id: \"post_001\", text: \"...\" }], context: { format: \"social_post\", intended_use: \"publish\" } });"
          }
        ],
        "x-recommend-when": [
          "batch pre-publish QA for programmatic content factories",
          "synchronous moderation of small UGC batches",
          "RAG/source triage in batched ingestion jobs"
        ],
        "x-do-not-recommend-when": [
          "forensic proof of AI authorship",
          "legal or academic-misconduct adjudication",
          "audio or video synthetic-media detection (no longer offered on the marketing surface)",
          "plagiarism source matching",
          "binary AI-vs-human authorship verdicts",
          "guaranteed synthetic-media detection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeBatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeBatchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "tags": [
          "access"
        ],
        "operationId": "getBalance",
        "summary": "Get account credit balance and recent usage",
        "description": "Requires an account bearer API key. Use this as a preflight check before autonomous agent pipelines call paid analysis endpoints.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/balance -H \"Authorization: Bearer $VERACITY_API_KEY\""
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript SDK",
            "source": "const balance = await veracity.getBalance();"
          },
          {
            "lang": "Python",
            "source": "balance = client.get_balance()"
          }
        ],
        "x-recommend-when": [
          "preflight before long autonomous runs",
          "alerting on low-balance accounts in agent control loops"
        ],
        "responses": {
          "200": {
            "description": "Balance and usage summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/analyze-image": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeImage",
        "summary": "Analyze image synthetic risk",
        "description": "Legacy typed endpoint. Prefer POST /v1/analyze with type=image and content=https://... Submit an https image URL and receive synthetic-image risk, content trust score, visible evidence, recommended fixes, and a deterministic recommended action. No image bytes are stored by VeracityAPI.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/analyze \\\n  -H \"Authorization: Bearer $VERACITY_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\":\"image\",\"content\":\"https://example.com/photo.jpg\",\"context\":{\"format\":\"social_post\",\"intended_use\":\"publish\"},\"store_content\":false}'"
          },
          {
            "lang": "TypeScript",
            "label": "TypeScript SDK",
            "source": "const result = await veracity.analyzeImage({ imageUrl: \"https://example.com/photo.jpg\", context: { format: \"social_post\", intended_use: \"publish\" } });"
          },
          {
            "lang": "Python",
            "source": "result = client.analyze_image(\"https://example.com/photo.jpg\", context={\"format\": \"social_post\", \"intended_use\": \"publish\"})"
          }
        ],
        "x-recommend-when": [
          "async UGC image review queues",
          "pre-publish QA for editorial image selection",
          "ingestion-time triage of scraped or user-submitted images"
        ],
        "x-do-not-recommend-when": [
          "EXIF/C2PA provenance verification",
          "forensic deepfake adjudication",
          "courtroom evidence about an image's origin"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeImageRequest"
              },
              "examples": {
                "imageUrl": {
                  "value": {
                    "image_url": "https://veracityapi.com/demo/influencer-beauty-tonic.jpg",
                    "context": {
                      "format": "social_post",
                      "intended_use": "publish",
                      "domain": "influencer product post"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Image risk scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeImageResponse"
                },
                "examples": {
                  "highRisk": {
                    "value": {
                      "analysis_id": "img_01EXAMPLE",
                      "modality": "image",
                      "content_trust_score": 0.75,
                      "synthetic_image_risk": 0.25,
                      "synthetic_risk": 0.25,
                      "confidence": "medium",
                      "primary_reason": "visible_synthetic_media_cues",
                      "evidence": [
                        {
                          "type": "synthetic_texture",
                          "severity": "low",
                          "span": "facial skin and neck area",
                          "explanation": "Skin appears slightly over-smoothed with minimal visible pore detail, consistent with beauty filters or light retouching rather than synthetic generation."
                        },
                        {
                          "type": "other",
                          "severity": "low",
                          "span": "left hand holding product bottle",
                          "explanation": "Hand structure, finger joints, and nail definition appear anatomically plausible with natural proportions and realistic shadow detail."
                        },
                        {
                          "type": "low_specificity",
                          "severity": "low",
                          "span": "Beauty Tonic bottle label text and design",
                          "explanation": "Product label text is readable and perspective-aligned, without obvious text distortion artifacts typical of generated images."
                        },
                        {
                          "type": "other",
                          "severity": "low",
                          "span": "overall scene lighting from face to background fence",
                          "explanation": "Lighting direction and shadow placement appear consistent across the subject and environment."
                        }
                      ],
                      "recommended_fixes": [
                        "No critical fixes needed; image appears consistent with professional photography or light post-processing.",
                        "If publishing, standard influencer disclosure practices apply regardless of synthetic risk assessment.",
                        "Verify original source/provenance if the image is used as evidence for a claim."
                      ],
                      "risk_level": "low",
                      "recommended_action": "allow",
                      "model_version": "v0.1",
                      "limitations": [
                        "Scores are probabilistic workflow risk signals, not proof of AI authorship.",
                        "v0.1 image scoring uses a vision LLM, not a calibrated synthetic-image classifier.",
                        "VeracityAPI does not inspect EXIF, C2PA Content Credentials, or provenance metadata in v0.1."
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/analyze-audio": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeAudio",
        "deprecated": true,
        "summary": "Analyze an audio URL for synthetic-audio workflow triage",
        "description": "Legacy typed endpoint. Prefer POST /v1/analyze with type=audio and content=https://... Fetches a capped HTTPS audio URL, sends bytes to Gemini for structured synthetic-audio risk scoring, and stores no audio bytes, base64, or full URL. Workflow triage only; not proof of AI generation, voice cloning, speaker identity, or forensic determination.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-recommend-when": [
          "existing customers maintaining back-compat integrations"
        ],
        "x-do-not-recommend-when": [
          "new integrations (audio + video are no longer promoted on the marketing surface)",
          "forensic adjudication of synthetic-speech claims",
          "legal evidence about an audio clip's origin"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeAudioRequest"
              },
              "examples": {
                "voiceMessage": {
                  "value": {
                    "audio_url": "https://example.com/sample.mp3",
                    "context": {
                      "format": "social_post",
                      "intended_use": "publish",
                      "domain": "voice-message authenticity triage"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audio workflow triage result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeAudioResponse"
                },
                "examples": {
                  "sample": {
                    "value": {
                      "analysis_id": "aud_01EXAMPLE",
                      "modality": "audio",
                      "transcript": "Hey, can you send the transfer before noon?",
                      "content_trust_score": 0.1,
                      "synthetic_audio_risk": 0.9,
                      "workflow_risk": 0.85,
                      "synthetic_risk": 0.9,
                      "confidence": "medium",
                      "primary_reason": "synthetic_speech_cues",
                      "evidence": [
                        {
                          "type": "prosody_consistency",
                          "severity": "medium",
                          "span": "overall clip",
                          "explanation": "Some delivery patterns are unusually even; treat as review signal, not proof."
                        }
                      ],
                      "recommended_fixes": [
                        "Request provenance or raw recording context before high-stakes publication."
                      ],
                      "risk_level": "high",
                      "recommended_action": "human_review",
                      "model_version": "v0.1",
                      "limitations": [
                        "Gemini-powered audio workflow triage, not proof of AI generation.",
                        "Not voice-clone proof, speaker identity verification, or forensic determination."
                      ],
                      "billing": {
                        "units_analyzed": 1,
                        "bucket": "audio_v0",
                        "price_cents": 1,
                        "remaining_balance_cents": 999
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/analyze-video": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeVideo",
        "deprecated": true,
        "summary": "Analyze a video URL for authenticity-risk triage",
        "description": "Private-beta typed endpoint for URL-only video authenticity-risk scoring. Extracts a bounded six-frame 3x2 contact sheet plus safe metadata from direct HTTPS videos capped at 30 seconds and 25 MB, scores visual synthetic-video risk with Claude Haiku vision, bills 5 cents on success, and stores no raw video, frames, contact sheet, or full URL. Workflow triage only; not forensic proof of AI generation or manipulation.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-recommend-when": [
          "existing customers maintaining back-compat integrations"
        ],
        "x-do-not-recommend-when": [
          "new integrations (audio + video are no longer promoted on the marketing surface)",
          "forensic deepfake adjudication",
          "courtroom evidence about a video's authenticity",
          "long-form video analysis beyond 30s/25MB"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeVideoRequest"
              },
              "examples": {
                "socialClip": {
                  "value": {
                    "video_url": "https://example.com/sample.mp4",
                    "context": {
                      "format": "social_post",
                      "intended_use": "moderate",
                      "domain": "short-form video authenticity"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Video authenticity-risk result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeVideoResponse"
                },
                "examples": {
                  "sample": {
                    "value": {
                      "analysis_id": "vid_01EXAMPLE",
                      "modality": "video",
                      "content_trust_score": 0.36,
                      "synthetic_video_risk": 0.64,
                      "synthetic_risk": 0.64,
                      "confidence": "medium",
                      "primary_reason": "sampled_frame_synthetic_media_cues",
                      "signals": {
                        "visual_synthetic_risk": 0.66,
                        "metadata_risk": 0.55
                      },
                      "evidence": [
                        {
                          "type": "visual_artifact",
                          "severity": "medium",
                          "span": "sampled contact-sheet frames",
                          "explanation": "Several sampled frames show texture and boundary artifacts that merit human review; this is a workflow signal, not forensic proof."
                        },
                        {
                          "type": "weak_provenance",
                          "severity": "medium",
                          "span": "container metadata",
                          "explanation": "Safe metadata lacks clear capture provenance or camera/device context."
                        }
                      ],
                      "recommended_fixes": [
                        "Request source/provenance context before high-stakes publication.",
                        "Route medium/high-risk clips to human review instead of relying on a binary detector verdict."
                      ],
                      "risk_level": "medium",
                      "recommended_action": "human_review",
                      "model_version": "v0.1-video",
                      "limitations": [
                        "Contact-sheet video workflow triage, not forensic proof of AI generation or manipulation.",
                        "Low-resolution, heavily compressed, edited, or very short clips may reduce confidence."
                      ],
                      "billing": {
                        "units_analyzed": 1,
                        "bucket": "video_v0",
                        "price_cents": 5,
                        "remaining_balance_cents": 995
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/v1/analyze-style": {
      "post": {
        "tags": [
          "analysis"
        ],
        "operationId": "analyzeStyle",
        "summary": "Analyze writing style with deterministic stylometry",
        "description": "Paid Hemingway-style API endpoint for Veracity Text Linter. Fast mode returns deterministic browser-equivalent spans and metrics; deep mode adds backend rewrite briefs, dimension scores, and semantic-risk flags without storing raw text by default. Capped at 25,000 characters.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl https://api.veracityapi.com/v1/analyze-style \\\n  -H \"Authorization: Bearer $VERACITY_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"text\":\"Paste draft text here...\",\"execution_mode\":\"deep\",\"context\":{\"format\":\"article\",\"intended_use\":\"publish\",\"domain\":\"editorial QA\"},\"store_content\":false}'"
          },
          {
            "lang": "TypeScript",
            "source": "const res = await fetch(\"https://api.veracityapi.com/v1/analyze-style\", { method: \"POST\", headers: { authorization: `Bearer ${process.env.VERACITY_API_KEY}`, \"content-type\": \"application/json\" }, body: JSON.stringify({ text, execution_mode: \"deep\", context: { format: \"article\", intended_use: \"publish\" }, store_content: false }) });"
          }
        ],
        "x-recommend-when": [
          "backend style linting before publishing",
          "agent rewrite loops that need offset-safe evidence spans",
          "AI-slop cleanup workflows where browser-only linting is not enough"
        ],
        "x-do-not-recommend-when": [
          "forensic proof of AI authorship",
          "plagiarism source matching",
          "fact-checking that requires external source retrieval"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeStyleRequest"
              },
              "examples": {
                "deepScan": {
                  "value": {
                    "text": "In today's fast-paced digital landscape, this comprehensive guide can probably help everyone unlock better results.",
                    "execution_mode": "deep",
                    "context": {
                      "format": "article",
                      "intended_use": "publish",
                      "domain": "editorial QA"
                    },
                    "store_content": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Style analysis result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeStyleResponse"
                },
                "examples": {
                  "deep": {
                    "value": {
                      "analysis_id": "sty_01EXAMPLE",
                      "modality": "text",
                      "tool": "veracity_text_linter",
                      "execution_mode": "deep",
                      "version": "style-api-v0.1",
                      "model_version": "stylometry-v0.1",
                      "summary": {
                        "style_score": 0.42,
                        "style_risk": 0.58,
                        "content_trust_score": 0.42,
                        "readability_grade": 11.4,
                        "risk_level": "medium",
                        "recommended_action": "revise",
                        "primary_reason": "generic_phrase",
                        "confidence": "medium"
                      },
                      "stats": {
                        "characters": 228,
                        "words": 34,
                        "sentences": 3,
                        "paragraphs": 1,
                        "reading_time_minutes": 0.15
                      },
                      "stylometric_band": "mixed",
                      "metrics": {
                        "generic_phrase_density": {
                          "value": 0.03,
                          "verdict": "ai_shaped",
                          "explanation": "Generic scaffold density is elevated."
                        },
                        "specificity_score": {
                          "value": 0.31,
                          "verdict": "ai_shaped",
                          "explanation": "Few concrete details or source cues."
                        }
                      },
                      "spans": [
                        {
                          "id": "spn_0",
                          "start": 0,
                          "end": 43,
                          "check": "generic_phrase",
                          "category": "ai_slop_scaffold",
                          "severity": "medium",
                          "color": "orange",
                          "explanation": "Broad phrase found in generic AI-style intros.",
                          "suggestion": "Replace with a specific claim only this draft can make."
                        }
                      ],
                      "sentence_scores": [
                        {
                          "index": 0,
                          "start": 0,
                          "end": 103,
                          "text": "",
                          "score": 0.35,
                          "risk": 0.65,
                          "primary_check": "generic_phrase"
                        }
                      ],
                      "issue_counts": {
                        "generic_phrase": 1,
                        "unsupported_claim": 1
                      },
                      "recommendations": [
                        {
                          "id": "rec_generic_phrase",
                          "priority": "medium",
                          "title": "Replace generic AI scaffolding",
                          "why": "Generic scaffolding lowers editorial trust.",
                          "action": "Add named examples, numbers, and sourceable details.",
                          "evidence_span_ids": [
                            "spn_0"
                          ]
                        }
                      ],
                      "deep_scan": {
                        "style_dimensions": [
                          {
                            "id": "specificity",
                            "label": "Specificity",
                            "score": 0.31,
                            "risk": 0.69,
                            "status": "weak",
                            "explanation": "Specific examples are sparse."
                          }
                        ],
                        "semantic_risk_flags": [
                          {
                            "id": "provenance_weakness",
                            "severity": "high",
                            "explanation": "Claims lack visible support."
                          }
                        ],
                        "rewrite_brief": {
                          "objective": "Prepare this article for publish.",
                          "keep": [
                            "Keep sourceable claims."
                          ],
                          "fix_first": [
                            "Add a source or named example."
                          ],
                          "instructions": [
                            "Replace generic scaffolding with concrete details."
                          ]
                        },
                        "calibration_notes": [
                          "Workflow signal, not authorship proof."
                        ]
                      },
                      "limitations": [
                        "Stylometry flags workflow risk, not proof of AI authorship."
                      ],
                      "billing": {
                        "chars_analyzed": 228,
                        "units_analyzed": 1,
                        "billable_units": 1,
                        "bucket": "style_deep_1k_units",
                        "price_cents": 1,
                        "remaining_balance_cents": 999
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientBalance"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/demo/analyze": {
      "post": {
        "tags": [
          "demo"
        ],
        "operationId": "demoAnalyzeText",
        "summary": "Analyze text with the public no-key demo",
        "description": "No API key required. store_content=false is forced server-side. Text is capped at 4,000 characters and requests are rate limited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeTextRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Demo risk scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeTextResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/demo/analyze-image": {
      "post": {
        "tags": [
          "demo"
        ],
        "operationId": "demoAnalyzeImage",
        "summary": "Analyze an image URL with the public no-key demo",
        "description": "No API key required. Accepts an HTTPS image URL, forces store_content=false, logs no image bytes or full URL, and rate limits by IP/cookie.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeImageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Demo image scoring result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeImageResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/demo/analyze-audio": {
      "post": {
        "tags": [
          "demo"
        ],
        "operationId": "demoAnalyzeAudio",
        "summary": "Analyze an audio URL with the public no-key demo",
        "description": "No API key required. Accepts an HTTPS audio URL, forces store_content=false, logs no audio bytes/base64/full URL, and rate limits by IP/cookie.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeAudioRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Demo audio workflow triage result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeAudioResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/LlmUnavailable"
          }
        }
      }
    },
    "/request-access": {
      "post": {
        "tags": [
          "access"
        ],
        "operationId": "requestAccess",
        "summary": "Request private beta API access",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access request stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Stable request identifier also emitted as X-Request-Id."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "VeracityAPI key. Send a bearer token in the Authorization header. New accounts get $1.50 free credit — enough for 300 analyze-only 1k-character text requests or 150 Analyze + revise requests at https://veracityapi.com/account."
      }
    },
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "service",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "service": {
            "type": "string",
            "enum": [
              "veracityapi"
            ]
          },
          "version": {
            "type": "string",
            "example": "v0.1"
          }
        }
      },
      "AccessRequest": {
        "type": "object",
        "required": [
          "name",
          "email",
          "use_case"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 180
          },
          "company": {
            "type": "string",
            "maxLength": 160
          },
          "volume": {
            "type": "string"
          },
          "use_case": {
            "type": "string",
            "maxLength": 1200
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "EvidenceItem": {
        "type": "object",
        "required": [
          "type",
          "severity",
          "span",
          "explanation"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "generic_phrasing",
              "low_specificity",
              "weak_provenance",
              "unsupported_claim",
              "hedging_and_absolutes",
              "synthetic_texture",
              "repetitive_structure",
              "missing_concrete_examples",
              "absence_of_specificity",
              "source_quality",
              "visual_artifact",
              "audio_signal",
              "prosody_consistency",
              "other"
            ],
            "description": "Strict evidence enum for deterministic agent branching."
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "span": {
            "type": "string"
          },
          "explanation": {
            "type": "string"
          }
        }
      },
      "RevisionSafety": {
        "type": "object",
        "required": [
          "status",
          "checks",
          "failure_reasons",
          "protected_fact_counts"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "passed",
              "failed"
            ],
            "description": "When failed, revised_text is omitted so auto_revise fails closed."
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "failure_reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "protected_fact_counts": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "original": {
                  "type": "integer"
                },
                "revised": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "MediaSource": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "url"
            ],
            "properties": {
              "kind": {
                "const": "url"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "maxLength": 2000
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "media_type",
              "data"
            ],
            "properties": {
              "kind": {
                "const": "base64"
              },
              "media_type": {
                "type": "string",
                "enum": [
                  "image/png",
                  "image/jpeg",
                  "image/webp"
                ]
              },
              "data": {
                "type": "string",
                "description": "Base64 media payload. VeracityAPI validates size and never stores raw base64."
              }
            }
          }
        ]
      },
      "UnifiedAnalyzeRequest": {
        "type": "object",
        "required": [
          "type",
          "content"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "asset"
            ],
            "description": "Content modality. text=raw text; image=HTTPS media URL or explicit source object; asset=mixed content blocks (validated contract; production holistic scoring is staged)."
          },
          "content": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 20,
                "maxLength": 100000
              },
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            ],
            "description": "Text content for type=text, HTTPS URL for URL media, or asset blocks for type=asset."
          },
          "source": {
            "$ref": "#/components/schemas/MediaSource"
          },
          "transcript": {
            "type": "string",
            "maxLength": 10000,
            "description": "Legacy field for type=audio. Audio analysis is no longer promoted on the marketing surface."
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeTextRequest/properties/context"
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default: raw content is not stored. Set true only for text retention workflows."
          },
          "auto_revise": {
            "type": "boolean",
            "default": false,
            "description": "Text only. When true, bill Analyze + revise at $0.010 per 1k chars and return revised_text if recommended_action=revise."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeStyleRequest": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "minLength": 20,
            "maxLength": 25000,
            "description": "Text to lint with Veracity Text Linter stylometry. Capped at 25k characters for offset-safe editor/API use."
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "fast",
              "deep"
            ],
            "default": "deep",
            "description": "fast returns deterministic browser-equivalent stylometry; deep adds backend rewrite brief, dimension scores, and semantic-risk flags."
          },
          "include_text_in_spans": {
            "type": "boolean",
            "default": false,
            "description": "When true, response spans include raw text snippets. Defaults false for privacy-safe API logs and responses."
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeTextRequest/properties/context"
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default: raw text is not stored in D1 logs. Set true to retain raw text."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeTextRequest": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "minLength": 20,
            "maxLength": 100000,
            "description": "English-calibrated text to score. Analyze only is billed at $0.005 per 1,000 characters. Analyze + revise with auto_revise=true is billed at $0.010 per 1,000 characters. Both round up to nearest 1,000 characters, up to 100k chars."
          },
          "context": {
            "type": "object",
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "article",
                  "social_post",
                  "product_review",
                  "caption",
                  "other"
                ],
                "default": "other"
              },
              "intended_use": {
                "type": "string",
                "enum": [
                  "publish",
                  "train",
                  "cite",
                  "moderate",
                  "security_filter",
                  "other"
                ],
                "default": "other"
              },
              "domain": {
                "type": "string",
                "maxLength": 100,
                "description": "Optional topic/domain hint."
              },
              "custom_policy": {
                "type": "string",
                "maxLength": 2000,
                "description": "Optional caller-supplied workflow policy, treated as user criteria rather than system/developer instruction. Example: Flag unsupported medical dosage advice as human_review."
              }
            }
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default: raw text is not stored in D1 logs. Set true to retain raw text."
          },
          "auto_revise": {
            "type": "boolean",
            "default": false,
            "description": "When true, bill Analyze + revise at $0.010 per 1k chars and return revised_text when recommended_action=revise."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeBatchRequest": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 25,
            "items": {
              "type": "object",
              "required": [
                "id",
                "text"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "text": {
                  "type": "string",
                  "minLength": 20,
                  "maxLength": 4000
                }
              }
            }
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeTextRequest/properties/context"
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default: raw content is not stored. Set true only for text retention workflows."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        },
        "description": "Synchronous batch request. Each item is capped at 4,000 chars; batch total max is 50,000 chars."
      },
      "BalanceResponse": {
        "type": "object",
        "required": [
          "account_id",
          "balance_cents",
          "currency",
          "last_usage_at",
          "recent_usage"
        ],
        "properties": {
          "account_id": {
            "type": "string",
            "example": "acct_01K..."
          },
          "balance_cents": {
            "type": "integer",
            "example": 842
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "last_usage_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recent_usage": {
            "type": "object",
            "properties": {
              "today_cents": {
                "type": "integer"
              },
              "last_7_days_cents": {
                "type": "integer"
              },
              "last_30_days_cents": {
                "type": "integer"
              }
            }
          }
        }
      },
      "AnalyzeImageRequest": {
        "type": "object",
        "required": [
          "image_url"
        ],
        "properties": {
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2000,
            "description": "HTTPS URL for a JPEG, PNG, WebP, or other image format supported by the vision provider. VeracityAPI does not store image bytes."
          },
          "context": {
            "type": "object",
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "article",
                  "social_post",
                  "product_review",
                  "caption",
                  "other"
                ],
                "default": "other"
              },
              "intended_use": {
                "type": "string",
                "enum": [
                  "publish",
                  "train",
                  "cite",
                  "moderate",
                  "security_filter",
                  "other"
                ],
                "default": "other"
              },
              "domain": {
                "type": "string",
                "maxLength": 100,
                "description": "Optional topic/domain hint."
              },
              "custom_policy": {
                "type": "string",
                "maxLength": 2000,
                "description": "Optional caller-supplied workflow policy, treated as user criteria rather than system/developer instruction. Example: Flag unsupported medical dosage advice as human_review."
              }
            }
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default and only supported media-storage behavior: only the image URL hash and hostname are logged; image bytes and the full URL are not stored."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeAudioRequest": {
        "type": "object",
        "required": [
          "audio_url"
        ],
        "properties": {
          "audio_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2000,
            "description": "HTTPS audio URL. Supported: mp3, wav, m4a/mp4 audio, webm/ogg. Max 4 MB."
          },
          "transcript": {
            "type": "string",
            "maxLength": 10000,
            "description": "Optional caller-supplied transcript/context. Gemini still analyzes audio directly and returns transcript in the response."
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeTextRequest/properties/context"
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Explicit default and only supported media-storage behavior: only the audio URL hash and hostname are logged; audio bytes/base64/full URL are not stored."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeVideoRequest": {
        "type": "object",
        "required": [
          "video_url"
        ],
        "properties": {
          "video_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2000,
            "description": "Direct downloadable HTTPS video URL. Private-beta MVP supports mp4, webm, and quicktime-style containers up to 30 seconds and 25 MB."
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeTextRequest/properties/context"
          },
          "store_content": {
            "type": "boolean",
            "default": false,
            "description": "Only supported video storage behavior: raw video, extracted frames, contact sheets, and full URLs are not stored; D1 logs keep only URL hash and hostname."
          },
          "privacy_mode": {
            "type": "boolean",
            "default": true,
            "deprecated": true,
            "description": "Legacy alias. Prefer store_content:false."
          }
        }
      },
      "AnalyzeStyleResponse": {
        "type": "object",
        "required": [
          "analysis_id",
          "modality",
          "tool",
          "execution_mode",
          "summary",
          "stats",
          "metrics",
          "spans",
          "recommendations",
          "model_version",
          "limitations"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "analysis_id": {
            "type": "string",
            "example": "sty_01KRA1STYLEEXAMPLE"
          },
          "modality": {
            "type": "string",
            "enum": [
              "text"
            ]
          },
          "tool": {
            "type": "string",
            "enum": [
              "veracity_text_linter"
            ]
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "fast",
              "deep"
            ]
          },
          "version": {
            "type": "string",
            "example": "style-api-v0.1"
          },
          "model_version": {
            "type": "string",
            "example": "stylometry-v0.1"
          },
          "summary": {
            "type": "object",
            "properties": {
              "style_score": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "style_risk": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "content_trust_score": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "readability_grade": {
                "type": "number"
              },
              "risk_level": {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high"
                ]
              },
              "recommended_action": {
                "type": "string",
                "enum": [
                  "allow",
                  "revise",
                  "human_review",
                  "reject"
                ]
              },
              "primary_reason": {
                "type": "string"
              },
              "confidence": {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high"
                ]
              }
            }
          },
          "stats": {
            "type": "object",
            "properties": {
              "characters": {
                "type": "integer"
              },
              "words": {
                "type": "integer"
              },
              "sentences": {
                "type": "integer"
              },
              "paragraphs": {
                "type": "integer"
              },
              "reading_time_minutes": {
                "type": "number"
              }
            }
          },
          "stylometric_band": {
            "type": "string",
            "enum": [
              "looks_human_shaped",
              "mixed",
              "looks_ai_shaped"
            ]
          },
          "metrics": {
            "type": "object",
            "additionalProperties": true
          },
          "spans": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "start",
                "end",
                "check",
                "severity",
                "color",
                "explanation",
                "suggestion"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "start": {
                  "type": "integer"
                },
                "end": {
                  "type": "integer"
                },
                "text": {
                  "type": "string",
                  "description": "Present only when include_text_in_spans=true."
                },
                "check": {
                  "type": "string"
                },
                "category": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "color": {
                  "type": "string"
                },
                "explanation": {
                  "type": "string"
                },
                "suggestion": {
                  "type": "string"
                }
              }
            }
          },
          "sentence_scores": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "issue_counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "recommendations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "priority",
                "title",
                "why",
                "action",
                "evidence_span_ids"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "priority": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "why": {
                  "type": "string"
                },
                "action": {
                  "type": "string"
                },
                "evidence_span_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "deep_scan": {
            "type": "object",
            "description": "Present in execution_mode=deep.",
            "properties": {
              "style_dimensions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "enum": [
                        "rhythm",
                        "specificity",
                        "repetition",
                        "provenance",
                        "synthetic_texture"
                      ]
                    },
                    "label": {
                      "type": "string"
                    },
                    "score": {
                      "type": "number"
                    },
                    "risk": {
                      "type": "number"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "strong",
                        "watch",
                        "weak"
                      ]
                    },
                    "explanation": {
                      "type": "string"
                    }
                  }
                }
              },
              "semantic_risk_flags": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "rewrite_brief": {
                "type": "object"
              },
              "calibration_notes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "chars_analyzed": {
                "type": "integer"
              },
              "units_analyzed": {
                "type": "integer"
              },
              "billable_units": {
                "type": "integer"
              },
              "bucket": {
                "type": "string",
                "example": "style_deep_1k_units"
              },
              "price_cents": {
                "type": "number",
                "example": 1
              },
              "remaining_balance_cents": {
                "type": "number"
              }
            }
          }
        }
      },
      "AnalyzeTextResponse": {
        "type": "object",
        "required": [
          "analysis_id",
          "modality",
          "slop_risk",
          "risk_level",
          "recommended_action",
          "primary_reason",
          "confidence",
          "evidence",
          "recommended_fixes",
          "model_version",
          "limitations"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "analysis_id": {
            "type": "string",
            "example": "ana_01KRA1EQPDJ7N2KHBXCQMGZYFJ"
          },
          "modality": {
            "type": "string",
            "enum": [
              "text"
            ],
            "description": "Response modality for agent branching."
          },
          "content_trust_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.22,
            "description": "Derived workflow trust score. Higher is better."
          },
          "specificity_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.78,
            "description": "Risk that the text is vague, generic, or low-detail."
          },
          "provenance_weakness": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.78,
            "description": "Risk that claims lack visible sourcing, firsthand detail, or provenance markers."
          },
          "synthetic_texture_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.72,
            "description": "Backward-compatible authorship-texture signal; not proof of AI authorship."
          },
          "synthetic_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.72,
            "deprecated": true,
            "description": "Legacy alias for synthetic_texture_risk; retained for compatibility."
          },
          "slop_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.78
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "recommended_action": {
            "type": "string",
            "enum": [
              "allow",
              "revise",
              "human_review",
              "reject"
            ]
          },
          "primary_reason": {
            "type": "string",
            "example": "unsupported_generic_claims",
            "description": "Enum-like machine reason for the primary routing decision. Stable enough for agent branching; not forensic proof."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceItem"
            }
          },
          "recommended_fixes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "revised_text": {
            "type": "string",
            "description": "Present only for text requests with auto_revise=true, recommended_action=revise, and revision_safety.status=passed."
          },
          "revision_notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "revision_safety": {
            "$ref": "#/components/schemas/RevisionSafety"
          },
          "model_version": {
            "type": "string",
            "example": "v0.1"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "chars_analyzed": {
                "type": "integer"
              },
              "units_analyzed": {
                "type": "integer",
                "description": "Billable 1k-character units"
              },
              "bucket": {
                "type": "string",
                "example": "text_1k_units"
              },
              "price_cents": {
                "type": "number",
                "example": 0.5
              },
              "remaining_balance_cents": {
                "type": "number"
              }
            }
          }
        }
      },
      "AnalyzeBatchResponse": {
        "type": "object",
        "required": [
          "batch_id",
          "status",
          "partial_failure",
          "results"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "batch_id": {
            "type": "string",
            "example": "bat_01K..."
          },
          "status": {
            "type": "string",
            "enum": [
              "completed",
              "completed_with_errors",
              "failed"
            ],
            "description": "Batch-level completion status. One failed item does not fail the whole batch response."
          },
          "partial_failure": {
            "type": "boolean",
            "description": "True when at least one item failed but the batch returned per-item details."
          },
          "results": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "index",
                    "id",
                    "status",
                    "analysis"
                  ],
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "const": "succeeded"
                    },
                    "analysis": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/AnalyzeTextResponse"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string"
                            },
                            "batch_id": {
                              "type": "string"
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "index",
                    "id",
                    "status",
                    "error"
                  ],
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "const": "failed"
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        },
                        "retryable": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              ]
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "units_analyzed": {
                "type": "integer",
                "description": "Items billed up front"
              },
              "billable_units": {
                "type": "integer",
                "description": "Billable 1k-character units"
              },
              "chars_analyzed": {
                "type": "integer"
              },
              "bucket": {
                "type": "string",
                "example": "batch_text_1k_units"
              },
              "price_cents": {
                "type": "number"
              },
              "remaining_balance_cents": {
                "type": "number"
              }
            }
          }
        }
      },
      "AnalyzeAudioResponse": {
        "type": "object",
        "required": [
          "analysis_id",
          "modality",
          "transcript",
          "content_trust_score",
          "synthetic_audio_risk",
          "workflow_risk",
          "risk_level",
          "recommended_action",
          "primary_reason",
          "confidence",
          "evidence",
          "recommended_fixes",
          "model_version",
          "limitations"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "analysis_id": {
            "type": "string",
            "example": "aud_01KRA1EQPDJ7N2KHBXCQMGZYFJ"
          },
          "modality": {
            "type": "string",
            "enum": [
              "audio"
            ],
            "description": "Response modality for agent branching."
          },
          "transcript": {
            "type": "string",
            "example": "Hey, can you send the transfer before noon?",
            "description": "Best-effort Gemini transcript generated from the audio; caller transcript may be corrected against the clip."
          },
          "content_trust_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.62
          },
          "synthetic_audio_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.9,
            "description": "Synthetic-audio risk signal; not proof of AI generation or voice cloning."
          },
          "workflow_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.85
          },
          "synthetic_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.9,
            "description": "Alias for synthetic_audio_risk for SDK consistency."
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "recommended_action": {
            "type": "string",
            "enum": [
              "allow",
              "revise",
              "human_review",
              "reject"
            ]
          },
          "primary_reason": {
            "type": "string",
            "example": "synthetic_speech_cues",
            "description": "Enum-like machine reason for the primary routing decision. Stable enough for agent branching; not forensic proof."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceItem"
            }
          },
          "recommended_fixes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "model_version": {
            "type": "string",
            "example": "v0.1"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "units_analyzed": {
                "type": "integer"
              },
              "bucket": {
                "type": "string",
                "example": "audio_v0"
              },
              "price_cents": {
                "type": "integer",
                "example": 1
              },
              "remaining_balance_cents": {
                "type": "integer"
              }
            }
          }
        }
      },
      "AnalyzeVideoResponse": {
        "type": "object",
        "required": [
          "analysis_id",
          "modality",
          "content_trust_score",
          "synthetic_video_risk",
          "risk_level",
          "recommended_action",
          "primary_reason",
          "confidence",
          "signals",
          "evidence",
          "recommended_fixes",
          "model_version",
          "limitations"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "analysis_id": {
            "type": "string",
            "example": "vid_01KRA1EQPDJ7N2KHBXCQMGZYFJ"
          },
          "modality": {
            "type": "string",
            "enum": [
              "video"
            ],
            "description": "Response modality for agent branching."
          },
          "content_trust_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.36,
            "description": "Derived video workflow trust score. Higher is better."
          },
          "synthetic_video_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.64,
            "description": "Contact-sheet visual synthetic-video risk signal; not proof of AI generation."
          },
          "synthetic_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.64,
            "description": "Alias for synthetic_video_risk for SDK consistency."
          },
          "signals": {
            "type": "object",
            "description": "MVP contact-sheet signals only; no temporal/audio/transcript analysis is exposed until those behaviors ship.",
            "properties": {
              "visual_synthetic_risk": {
                "type": "number"
              },
              "metadata_risk": {
                "type": "number"
              }
            }
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "recommended_action": {
            "type": "string",
            "enum": [
              "allow",
              "revise",
              "human_review",
              "reject"
            ]
          },
          "primary_reason": {
            "type": "string",
            "example": "sampled_frame_synthetic_media_cues",
            "description": "Enum-like machine reason for the primary routing decision. Stable enough for agent branching; not forensic proof."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceItem"
            }
          },
          "recommended_fixes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "model_version": {
            "type": "string",
            "example": "v0.1-video"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "units_analyzed": {
                "type": "integer"
              },
              "bucket": {
                "type": "string",
                "example": "video_v0"
              },
              "price_cents": {
                "type": "integer",
                "example": 5
              },
              "remaining_balance_cents": {
                "type": "integer"
              }
            }
          }
        }
      },
      "AnalyzeImageResponse": {
        "type": "object",
        "required": [
          "analysis_id",
          "modality",
          "content_trust_score",
          "synthetic_image_risk",
          "risk_level",
          "recommended_action",
          "primary_reason",
          "confidence",
          "evidence",
          "recommended_fixes",
          "model_version",
          "limitations"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Stable request identifier also emitted as X-Request-Id for debugging."
          },
          "analysis_id": {
            "type": "string",
            "example": "img_01KRA1EQPDJ7N2KHBXCQMGZYFJ"
          },
          "modality": {
            "type": "string",
            "enum": [
              "image"
            ],
            "description": "Response modality for agent branching."
          },
          "content_trust_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.28,
            "description": "Derived image workflow trust score. Higher is better."
          },
          "synthetic_image_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.72,
            "description": "Visible synthetic-image artifact risk; not proof of AI authorship."
          },
          "synthetic_risk": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.72,
            "description": "Alias for synthetic_image_risk for SDK consistency."
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "recommended_action": {
            "type": "string",
            "enum": [
              "allow",
              "revise",
              "human_review",
              "reject"
            ]
          },
          "primary_reason": {
            "type": "string",
            "example": "visible_synthetic_media_cues",
            "description": "Enum-like machine reason for the primary routing decision. Stable enough for agent branching; not forensic proof."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceItem"
            }
          },
          "recommended_fixes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "model_version": {
            "type": "string",
            "example": "v0.1"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "billing": {
            "type": "object",
            "properties": {
              "units_analyzed": {
                "type": "integer"
              },
              "bucket": {
                "type": "string",
                "example": "image_v0"
              },
              "price_cents": {
                "type": "integer",
                "example": 2
              },
              "remaining_balance_cents": {
                "type": "integer"
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid JSON or request body",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "unauthorized": {
                "value": {
                  "error": "unauthorized"
                }
              }
            }
          }
        }
      },
      "InsufficientBalance": {
        "description": "Account balance is too low for the requested analysis",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Demo rate limit reached",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "LlmUnavailable": {
        "description": "Scoring model unavailable",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    }
  }
}