{
  "openapi": "3.1.0",
  "info": {
    "title": "VeracityAPI",
    "version": "0.1.0",
    "summary": "Founder-led content trust scoring API for agents",
    "description": "Call this endpoint immediately before publishing generated drafts or ingesting unverified text, image URLs, audio URLs, and private-beta video URLs. Scores content trust, specificity/slop risk, synthetic image/audio/video 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"
    }
  },
  "servers": [
    {
      "url": "https://api.veracityapi.com",
      "description": "Production API"
    },
    {
      "url": "https://veracityapi.com",
      "description": "Public demo host"
    }
  ],
  "tags": [
    {
      "name": "analysis",
      "description": "Unified text, image, audio, and video 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, images, audio, or URLs from an unverified user. Send { type: 'text'|'image'|'audio', 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": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 180
            },
            "description": "Optional retry key. Successful /v1/analyze responses are cached for 24h per account, endpoint, and key; exact retries replay without a second debit."
          }
        ],
        "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"
                    }
                  }
                },
                "image": {
                  "value": {
                    "type": "image",
                    "content": "https://veracityapi.com/demo/influencer-beauty-tonic.jpg",
                    "context": {
                      "format": "social_post",
                      "intended_use": "publish",
                      "domain": "influencer product post"
                    }
                  }
                },
                "audio": {
                  "value": {
                    "type": "audio",
                    "content": "https://veracityapi.com/assets/demo-voice-message.mp3",
                    "context": {
                      "format": "social_post",
                      "intended_use": "publish",
                      "domain": "voice-message authenticity triage"
                    }
                  }
                }
              }
            }
          }
        },
        "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-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": []
          }
        ],
        "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": []
          }
        ],
        "responses": {
          "200": {
            "description": "Balance and usage summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/demo/analyze": {
      "post": {
        "tags": [
          "demo"
        ],
        "operationId": "demoAnalyzeText",
        "summary": "Analyze text with the public no-key demo",
        "description": "No API key required. raw content is not stored 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"
          }
        }
      }
    },
    "/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"
          }
        }
      },
      "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",
                  "audio/mpeg",
                  "audio/wav",
                  "audio/x-wav",
                  "audio/mp4",
                  "audio/m4a",
                  "audio/webm",
                  "audio/ogg"
                ]
              },
              "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",
              "audio",
              "video"
            ],
            "description": "Content modality. text=raw text; image/audio/video=HTTPS media URL."
          },
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100000,
            "description": "Text content for type=text, HTTPS URL for image/audio/video."
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeContext"
          },
          "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."
          }
        }
      },
      "AnalyzeContext": {
        "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",
              "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."
          },
          "transcript": {
            "type": "string",
            "maxLength": 10000,
            "description": "Audio only: optional caller-supplied transcript/context."
          }
        }
      },
      "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",
                  "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."
              }
            }
          },
          "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."
          }
        }
      },
      "AnalyzeBatchRequest": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 25,
            "description": "1-25 text items",
            "items": {
              "type": "object",
              "required": [
                "content"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "text"
                  ],
                  "default": "text"
                },
                "content": {
                  "type": "string",
                  "minLength": 20,
                  "maxLength": 4000
                },
                "context": {
                  "$ref": "#/components/schemas/AnalyzeContext"
                },
                "auto_revise": {
                  "type": "boolean",
                  "default": false
                }
              }
            }
          },
          "context": {
            "$ref": "#/components/schemas/AnalyzeContext"
          }
        },
        "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",
                  "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."
              }
            }
          }
        }
      },
      "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/AnalyzeContext"
          }
        }
      },
      "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/AnalyzeContext"
          }
        }
      },
      "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 when recommended_action=revise."
          },
          "revision_notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "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"
            }
          }
        }
      }
    }
  }
}