Back to Documentation

Sources & Auditing

Verifact evaluates claims only against the sources you provide. Verification and authorization are deterministic by default and do not fetch external URLs unless explicitly enabled. Verifact also emits signed, chained audit records (Ed25519; linked by prev_record_hash) for verify/authorize outcomes.

Sources (mental model)

Think of sources as the only evidence Verifact is allowed to use. You can provide sources inline with a request, or you can ingest them once via the Sources API and reference them repeatedly using source_id. For URL content, the recommended model is two-step: ingest the URL via POST /v2/sources (fetch + extract + persist), then use the returned source_id in verify/authorize.

Source APIs

Use the Sources API to ingest content (PDFs & URLs) and get stable source_id values that can be referenced in later requests.

POST /v2/sources

Ingest one or more sources for later reuse (JSON or multipart upload). Returns stable source_id values and content-level checksum hashes.

Use when you want to store content once (including URL content) and reference it deterministically in verification/authorization.

GET /sources/{source_id}

Retrieve a stored source by ID (v1 read endpoint). v2 ingestion uses /v2/sources.

Useful for debugging and reproducing verification decisions by inspecting the persisted canonical content and metadata.

Note — URL sources

For URL content, ingest via POST /v2/sources and reference the returned source_id. By default, verification/authorization won't fetch URLs; set options.allow_network to opt in.

How Sources Work

  • Only sources included in the request are considered
  • LLM output is never treated as evidence.
  • No inference beyond the supplied content.
  • Verification is separate from authorization; auditing records decisions so you can prove what happened.
Note — provide explicit claims

For deterministic results, provide explicit claims as separate items (one claim per statement). When you use authorize-output, Verifact may extract simple claims deterministically from text, but extraction is intentionally conservative and does not perform semantic inference. For precise verification, prefer explicit claims.

Audit guarantees

  • Signed records: each audit record includes a record_hash and is signed by the server's signing key (Ed25519).
  • Hash chaining: records link via prev_record_hash, enabling detection of missing or reordered records.
  • Proof visibility: proofs and minimal audit handles may be included in responses when available. Use the audit endpoints to retrieve canonical proofs and public keys for verification.

Request examples

Example 1 — verify with inline claims and inline source content

POST /v2/verify
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "claims": [
    { "id": "c1", "text": "We can refund purchases within 30 days of purchase." }
  ],
  "sources": [
    {
      "source_id": "s1",
      "type": "text",
      "title": "Refund Policy",
      "content": "Refunds are permitted within 30 days of purchase."
    }
  ]
}

Example 2 — reference a stored source_id

POST /v2/verify
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "claims": [ { "id": "c1", "text": "Refunds are allowed within 30 days." } ],
  "sources": [ { "source_id": "src_abc123" } ]
}

Example 3 — ingest a URL as a source (two-step model)

# Step 1: ingest (server fetch + extract + persist)
POST /v2/sources
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "sources": [
    {
      "type": "html",
      "title": "Refund Policy",
      "uri": "https://example.com/policies/refund-v3.html"
    }
  ]
}

# Response (example)
{
  "sources": [
    { "source_id": "src_abc123", "type": "html", "uri": "https://example.com/policies/refund-v3.html", "checksum": "sha256:..." }
  ],
  "errors": []
}

# Step 2: verify using the stored source_id (deterministic by default)
POST /v2/verify
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "claims": [ { "id": "c1", "text": "Refunds are allowed within 30 days." } ],
  "sources": [ { "source_id": "src_abc123" } ]
}

Optional — allow URL fetching during verify (explicit opt-in)

POST /v2/verify
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "claims": [ { "id": "c1", "text": "Refunds are allowed within 30 days." } ],
  "sources": [
    { "type": "html", "title": "Refund Policy", "uri": "https://example.com/policies/refund-v3.html" }
  ],
  "options": { "allow_network": true }
}

Upload PDF Example (multipart)

# cURL (multipart)
curl -X POST 'https://verifact-api.fly.dev/v2/sources' \
  -H 'Authorization: Bearer REPLACE_WITH_API_KEY' \
  -F 'file=@document.pdf' \
  -F 'title=Refund policy'

# Node.js (fetch + form-data)
import fs from 'fs';
import FormData from 'form-data';

const form = new FormData();
form.append('file', fs.createReadStream('document.pdf'));
form.append('title', 'Refund policy');

const res = await fetch('https://verifact-api.fly.dev/v2/sources', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer REPLACE_WITH_API_KEY', ...form.getHeaders() },
  body: form
});
console.log(await res.json());

# Python (requests)
import requests

with open('document.pdf', 'rb') as f:
  files = {'file': ('document.pdf', f, 'application/pdf')}
  data = {'title': 'Refund policy'}
  res = requests.post(
  'https://verifact-api.fly.dev/v2/sources',
    headers={'Authorization': 'Bearer REPLACE_WITH_API_KEY'},
    files=files,
    data=data
  )
  print(res.json())

# Response (example)
{
  "sources": [
    {
      "source_id": "src_abcdef123456",
      "type": "pdf",
      "title": "Refund policy",
      "checksum": "sha256:...",
      "status": "created",
      "bytes": 123456
    }
  ],
  "errors": []
}

Note: uploads are size-limited and rate-limited. Extraction failures are reported per-source in errors.

Audit proof example

Audit handles may be returned inline when available; use the audit endpoints below to fetch canonical proofs and public keys.

Compact (default response; audit handle may be present)

{
  "request_id": "req_abc123",
  "request_hash": "sha256:abc123",
  "verdict": "supported",
  "coverage_score": 1.0,
  "checks": [
    {
      "claim_id": "c1",
      "claim_text": "Paris is the capital of France",
      "coverage": 1.0,
      "citations": [ { "source_id": "s1", "quote": "Paris is the capital of France", "start": 0, "end": 29 } ]
    }
  ],
  "metadata": {
    "audit": {
      "record_id": "99ed92e7-8aaf-4e0f-b1b3-1a2b3c4d5e6f",
      "record_hash": "sha256:32514406a64...",
      "key_id": "audit_v1",
      "merkle_root": "sha256:5b0ab5785e0b...",
      "proof": [ { "hash": "sha256:aaa111...", "position": "left" } ]
    }
  },
  "errors": []
}

Verbose (per-claim details)

{
  "request_id": "req_abc123",
  "request_hash": "sha256:abc123",
  "verdict": "supported",
  "coverage_score": 1.0,
  "checks": [
    {
      "claim_id": "c1",
      "claim_text": "Paris is the capital of France",
      "coverage": 1.0,
      "citations": [ { "source_id": "s1", "quote": "Paris is the capital of France", "start": 0, "end": 29 } ]
    }
  ],
  "metadata": {
    "audit": {
      "record_id": "99ed92e7-8aaf-4e0f-b1b3-1a2b3c4d5e6f",
      "record_hash": "sha256:32514406a64...",
      "key_id": "audit_v1"
    }
  },
  "errors": []
}

How to request verbose responses

POST /v2/verify
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "claims": [ { "id": "c1", "text": "We can refund purchases within 30 days of purchase." } ],
  "sources": [
    { "type": "text", "title": "Refund Policy", "content": "Refunds are permitted within 30 days of purchase." }
  ],
  "response": { "mode": "verbose" }
}

Note: response.mode = "verbose" returns additional per-claim detail; for canonical proofs and signatures, use the audit endpoints.

How to verify a proof

  1. Extract record_id and record_hash from metadata.audit.
  2. Fetch the canonical proof (includes the batch signature when available) from GET /v2/audit/{record_id}/proof.
  3. Fetch the public key for key_id via GET /v2/audit/keys/{key_id}.
  4. Verify the signature over the returned merkle_root using Ed25519 and the returned public key; then verify inclusion via the proof.
View API Reference