{"openapi":"3.1.0","info":{"title":"VerifyAPI","version":"0.3.20","description":"Per-claim fact verification for AI agents. POST a claim, get back a verdict backed by a verbatim quote from a real source. Every response is a signed receipt (JWS/Ed25519) verifiable against /.well-known/jwks.json. Three payment paths: free trial (no signup), prepaid API key, or x402 (USDC on Base, keyless per-call). Three freshness tiers so agents can trade money for currency of the answer.","contact":{"name":"VerifyAPI","url":"https://verify-api.dev"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://api.verify-api.dev","description":"Production"},{"url":"http://localhost:8080","description":"Local dev"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"csk_live_… or csk_test_…","description":"API key issued by VerifyAPI. Live keys are prefixed `csk_live_`, test keys `csk_test_`."}},"schemas":{"VerifyRequest":{"type":"object","required":["claim"],"properties":{"claim":{"type":"string","minLength":1,"maxLength":1000,"description":"A single factual claim to verify. If you have multiple claims, either send each separately or POST /v1/verify/batch.","example":"The iPhone 15 launched on September 22, 2023."},"as_of":{"type":"string","format":"date","description":"Optional ISO date. Set for time-sensitive claims. Buckets into perennial / annual / volatile based on claim shape."},"max_wait_ms":{"type":"integer","minimum":500,"maximum":30000,"default":15000,"description":"Soft deadline. If the pipeline cannot finish within this window, returns insufficient_evidence (unbilled)."},"domains_allow":{"type":"array","items":{"type":"string","format":"hostname"},"description":"Optional allowlist of source hostnames."},"domains_deny":{"type":"array","items":{"type":"string","format":"hostname"},"description":"Optional denylist of source hostnames."},"freshness":{"type":"string","enum":["cached","fresh"],"description":"Freshness tier. `cached` (default) honors both cache tiers. `fresh` bypasses both caches, forces a full pipeline run, and is billed at the fresh-tier multiplier (default 5x). See GET /v1/pricing for current multipliers."},"max_age_seconds":{"type":"integer","minimum":0,"maximum":31536000,"description":"HTTP Cache-Control-style max-age for the verdict. Under FRESH_THRESHOLD_SEC (default 3600) triggers the fresh tier; under RECENT_THRESHOLD_SEC (default 86400) triggers the recent tier (default 2x); otherwise treated as cached."}},"additionalProperties":false},"TrialRequest":{"type":"object","required":["claim"],"description":"Free-tier trial input. Subset of VerifyRequest — no freshness knobs or domain filters. Trials always run at the cached tier so common claims cost the caller nothing and cost us nothing (cache hits).","properties":{"claim":{"type":"string","minLength":3,"maxLength":1000}},"additionalProperties":false},"Verdict":{"type":"object","required":["id","verdict","confidence","cache","billed_usd","as_of_bucket"],"properties":{"id":{"type":"string","description":"Verdict ULID, prefixed vrf_.","example":"vrf_01J8Z3W9YQKTB7ZK7C6E7N9J5R"},"verdict":{"type":"string","enum":["supported","refuted","insufficient_evidence","out_of_scope","malformed"]},"confidence":{"type":"number","minimum":0,"maximum":1,"description":"0-1. Below CONFIDENCE_FLOOR (default 0.75) is downgraded to insufficient_evidence."},"source_url":{"type":"string","format":"uri","nullable":true,"description":"URL of the source that yielded the verdict."},"published_date":{"type":"string","format":"date","nullable":true},"exact_quote":{"type":"string","nullable":true,"description":"Verbatim passage from the source. Guaranteed to appear character-for-character in the extracted source text."},"reasoning":{"type":"string","nullable":true,"description":"1-2 sentence explanation of the verdict."},"cache":{"type":"string","enum":["miss","exact","semantic"],"description":"Which cache tier served this verdict, if any. Semantic hits are cosine ≥ 0.92 within the same as_of bucket."},"billed_usd":{"type":"number","description":"Amount billed for this call, in dollars. 0 for insufficient_evidence / out_of_scope / malformed. May exceed base price at recent/fresh tiers."},"as_of_bucket":{"type":"string","description":"The temporal bucket the claim was assigned to.","example":"annual:2026-Q3"},"freshness_tier":{"type":"string","enum":["default","recent","fresh"],"description":"Which freshness tier the pipeline actually ran at. May differ from the requested tier when the resolver downgrades (e.g. cache hit within max_age_seconds)."},"price_multiplier":{"type":"number","description":"Multiplier applied to the base price for this verdict. 1 for default tier, 2 for recent, 5 for fresh (see /v1/pricing)."},"verdict_age_seconds":{"type":"integer","nullable":true,"description":"Age of the verdict in seconds when it was returned. 0 for miss (fresh compute). Non-zero for cache hits."},"receipt":{"type":"object","description":"Signed receipt (compact JWS, EdDSA/Ed25519). Verify against /.well-known/jwks.json to prove authenticity without trusting the network path.","properties":{"jws":{"type":"string","description":"Compact JWS: `header.payload.signature`."},"kid":{"type":"string","description":"Key id — look up in /.well-known/jwks.json."},"issued_at":{"type":"string","format":"date-time"}}}}},"Problem":{"type":"object","description":"RFC 9457 problem-details.","required":["type","title","status"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"retry_after_ms":{"type":"integer"}}},"Pricing":{"type":"object","required":["price_per_verified_usd","min_topup_usd","billable_verdicts"],"properties":{"price_per_verified_usd":{"type":"number","example":0.02},"min_topup_usd":{"type":"number","example":5},"currency":{"type":"string","example":"USD"},"billable_verdicts":{"type":"array","items":{"type":"string"},"example":["supported","refuted"]},"free_verdicts":{"type":"array","items":{"type":"string"},"example":["insufficient_evidence","out_of_scope","malformed"]}}},"Balance":{"type":"object","properties":{"account_id":{"type":"string"},"balance_usd":{"type":"number"},"balance_micros":{"type":"integer"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"RateLimited":{"description":"Too many requests.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"PaymentRequired":{"description":"Two cases: (a) Account balance is zero or negative — top up at POST /v1/balance/topup. (b) x402 mode — body includes `x402Version:1` and `accepts:[PaymentRequirements]` with per-tier offers (default/recent/fresh). Sign an EIP-3009 authorization for the tier you want, base64-encode the payload, and retry with the X-PAYMENT header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"type":"object","properties":{"x402Version":{"type":"integer","example":1},"accepts":{"type":"array","items":{"type":"object","description":"x402 PaymentRequirements (Coinbase spec)."}}}}}}}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128},"description":"Client-generated idempotency key. Repeats within 24h return the original response. Body-mismatch on repeat returns 409."}}},"paths":{"/v1/verify":{"post":{"summary":"Verify a single claim.","description":"The pipeline: normalize → cache lookup (exact then semantic) → search → extract → adjudicate → verbatim-check → persist + bill. Returns within max_wait_ms or downgrades to insufficient_evidence.","operationId":"verifyClaim","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyRequest"}}}},"responses":{"200":{"description":"Verdict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verdict"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/verify/trial":{"post":{"summary":"Free trial verify — no key, no payment.","description":"Runs the full verify pipeline for a single claim without an API key or payment. Rate-limited per caller IP (default 3 calls per rolling 24h). Trials always use the cached tier so common claims serve from cache. Response is identical to /v1/verify plus `trial:true`, `trial_calls_used`, `trial_calls_limit`, `trial_resets_at`, and an `upgrade` block pointing at the signup and x402 paths. Signed receipts are marked with the shared `trial_shared` sub.","operationId":"verifyClaimTrial","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrialRequest"}}}},"responses":{"200":{"description":"Verdict + trial metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verdict"}}}},"429":{"description":"Trial rate limit exceeded. See `Retry-After` and `x-ratelimit-*` headers.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/discover":{"get":{"summary":"One-hop discovery manifest for agents.","description":"Single call an agent can make against the root API to learn every well-known URL, every route, every payment method, and current pricing tiers. Designed so an agent that has only the base URL can be fully bootstrapped in one round-trip.","operationId":"discover","security":[],"responses":{"200":{"description":"Discovery manifest.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/verify/batch":{"post":{"summary":"Verify up to 20 claims in one call.","description":"Runs each claim through the same pipeline concurrently (bounded). Cache hits are shared across the batch. Billed per successful verdict; free verdicts remain free.","operationId":"verifyBatch","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["claims"],"properties":{"claims":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/VerifyRequest"}}}}}}},"responses":{"200":{"description":"Array of verdicts, same order as input.","content":{"application/json":{"schema":{"type":"object","required":["verdicts","billed_usd"],"properties":{"verdicts":{"type":"array","items":{"$ref":"#/components/schemas/Verdict"}},"billed_usd":{"type":"number"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/pricing":{"get":{"summary":"Machine-readable pricing.","description":"No auth. Agents should poll this to decide whether the tool fits their budget.","operationId":"getPricing","security":[],"responses":{"200":{"description":"Pricing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pricing"}}}}}}},"/v1/balance":{"get":{"summary":"Get current balance.","operationId":"getBalance","responses":{"200":{"description":"Balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/balance/topup":{"post":{"summary":"Create a Stripe Checkout session to add credit.","operationId":"createTopup","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_usd"],"properties":{"amount_usd":{"type":"number","minimum":5},"success_url":{"type":"string","format":"uri"},"cancel_url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"Checkout session URL.","content":{"application/json":{"schema":{"type":"object","properties":{"checkout_url":{"type":"string","format":"uri"},"session_id":{"type":"string"}}}}}}}}},"/v1/stripe/webhook":{"post":{"summary":"Stripe webhook receiver.","operationId":"stripeWebhook","security":[],"description":"Called by Stripe. Verifies the signature, credits the ledger on successful checkout, records the event id to defend against replays.","responses":{"200":{"description":"Acknowledged."}}}},"/llms.txt":{"get":{"summary":"LLM-facing service manifest.","description":"Plain-text manifest following the emerging llms.txt convention. What this service is, when to use it, how to authenticate, what it costs, and links to every machine-readable endpoint. Served at the API root so LLM crawlers and agent operators can discover the service without HTML scraping.","operationId":"llmsTxt","security":[],"responses":{"200":{"description":"Plain text.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/health":{"get":{"summary":"Liveness probe.","operationId":"health","security":[],"responses":{"200":{"description":"OK."}}}}}}