Sources & Auditing
Factara evaluates claims only against the sources you provide. Verification and authorization are deterministic by default and do not fetch external URLs unless explicitly enabled. Factara 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 Factara 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 /v1/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.
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.
For deterministic results, provide explicit claims as separate items (one claim per statement). When you use authorize-output, Factara may extract simple claims deterministically from text, but extraction is intentionally conservative and does not perform semantic inference. For precise verification, prefer explicit claims.
Truth source example (recommended format)
For authorization to be reliable, upload a truth source document that lists the facts your system is allowed to rely on. Factara verifies each explicit claim against this document and returns citations to the exact lines that support or contradict it.
- Make statements citable: use stable IDs like
R1,R2so citations are durable. - One fact per line: keep each statement atomic (avoid bundling multiple conditions into one sentence).
- Define terms (optional): add a short Definitions section for ambiguous terms (e.g., “duplicate charge”).
- State exceptions explicitly: write exceptions as first-class statements (not implied by examples).
- Be concrete: prefer numbers, roles, and conditions over “usually” / “generally”.
- Version it: include a version and date so audit trails can show which facts were used.
Example truth source (.txt)
TITLE: Refund & Payments Policy OWNER: Customer Support / Finance VERSION: 1.3 EFFECTIVE_DATE: 2026-01-23 [WHAT FACTARA DOES / DOES NOT DO] - Factara verifies claims against provided evidence + this policy. - Factara does NOT compute business logic (date math, thresholds, "business days", etc.). - The agent/app MUST compute derived values and send them as explicit claims. [DEFINITIONS] (optional) D1. "Purchase date" means the timestamp on the payment processor charge. D2. "Duplicate charge" means two successful charges for the same order_id and same amount within 24 hours. D3. "Manager approval" means approval by a user with role=Support_Manager. [AGENT → CLAIMS CONTRACT] The refund agent MUST include: Evidence fields (raw): - order_id - charge_id - purchase_date (timestamp from processor) - refund_amount (numeric) and currency - digital_download_accessed (bool) - manager_approval_present (bool) OR approver_role Derived claims (computed by agent/app, then verified by Factara): - days_since_purchase (integer) - within_refund_window (bool) // derived from days_since_purchase <= 30 - duplicate_confirmed (bool) - duplicate_charge_ids (array of 2 charge_ids when duplicate_confirmed=true) - duplicate_charge_amount (numeric) - refund_amount_equals_duplicate_amount (bool) NOTE: For Processing SLA (business days), model submission as a separate action. Derived claims for submission step: - business_days_since_approval (integer) - within_processor_sla (bool) // derived from business_days_since_approval <= 5 [RULES] R1. Refund eligibility window: a refund is allowed only if within_refund_window = true. R2. Amount threshold: if refund_amount > 500.00 USD, manager approval is required (manager_approval_present = true). R3. Duplicate-charge refund: if duplicate_confirmed = true, refund_amount MUST equal duplicate_charge_amount. R4. Digital downloads: refunds are NOT allowed if digital_download_accessed = true. R5. Processing SLA (submission action): submission allowed only if within_processor_sla = true. R6. Required evidence for any refund: request MUST include order_id and charge_id. R7. Required evidence for a duplicate-charge refund: request MUST include duplicate_charge_ids (2 charge_ids) for the same order_id.
Upload/ingest this document as a source (e.g., POST /v2/sources), then include its source_id alongside case-specific evidence (tickets, payment logs) in your authorize request. Factara will verify explicit claims against the truth source statements and return citations to the relevant statement IDs (e.g., R1–R6, A1–A2).
Audit guarantees
- Signed records: each audit record includes a
record_hashand 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://factara-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://factara-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://factara-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
- Extract
record_idandrecord_hashfrommetadata.audit. - Fetch the canonical proof (includes the batch signature when available) from
GET /v2/audit/{record_id}/proof. - Fetch the public key for
key_idviaGET /v2/audit/keys/{key_id}. - Verify the signature over the returned
merkle_rootusing Ed25519 and the returned public key; then verify inclusion via the proof.