sourced.run — playground & hosted API

Run the engine

A public, stateless instance of @sourcedhq/core. Paste claims below and read the verdicts, or call POST /api/assess from your own code — full reference further down.

endpoint POST https://sourced.run/api/assess auth none CORS open limits 200 claims · 200 kB / call

1Playground

The prefilled example demonstrates the guarantees: four independent origins report the same event (count climbs to CONFIRMED), a repeat from reuters adds nothing (G3), and the single-origin story stays bare (G5). Edit freely — it is the live engine.

2What a call computes

Each call is batch-scoped and stateless: corroboration is computed across the claims you send in that request (titles that describe the same event merge via the dual gate; origins are counted distinct). Nothing is stored between calls — firstSeenAt equals the request time, and no data you send is retained.

That makes this endpoint ideal for evaluating the engine and for “given these N reports, what corroborates what?” batch questions. If you need persistent event memory — first-seen timelines that survive across runs — run @sourcedhq/core yourself with a store (one load()/save() pair, any KV; see the spec §3). A hosted persistent tier is on the roadmap.

Grouping hint: the engine merges events by title similarity (spec §4). If you already know which claims describe the same event — from your own clustering — pass clusters and skip the guesswork; Sourced then only counts.

3Request schema

POST /api/assess with content-type: application/json:

FieldTypeSemantics
claims requiredClaim[] (1–200)The batch to assess. Order is preserved in the response.
claims[].id requiredstringStable report id; used to look up clusters entries.
claims[].title requiredstringThe claim in words — event identity derives from it.
claims[].origin requiredstringThe source; the unit of independence. Normalize consistently.
claims[].publishedAt requiredstring, ISO 8601Upstream publish time; drives the “breaking” window. Unparseable → that claim can’t be “breaking”, everything else works.
clusters optional{ [claimId]: string[] }Your own event grouping: claim id → all origins reporting that event in this batch (≤ 100 origins per entry). Duplicates collapse (G3).
config optionalobjectThreshold overrides — whitelisted numeric keys only, clamped to sane ranges (§5).

4Response schema

200 OK, JSON:

FieldTypeSemantics
verdicts(Verdict | null)[]One entry per input claim, in input order. null = unassessable (e.g. title had no meaning-bearing tokens) — the claim passes through unlabeled, per G7.
verdicts[].corroborationnumber ≥ 1Distinct independent origins for the claim’s event within this batch.
verdicts[].corroboratingSourcesstring[]Receipts — the other origins (never the claim’s own), max 6 by default.
verdicts[].firstSeenAtstring, ISO 8601Stateless endpoint → the request time. (Persistent first-seen requires running the engine with a store.)
verdicts[].signal"confirmed" · "breaking" · "developing" · nullSee spec §4.4. There is deliberately no “true” and no veracity score (G2).
enginestringEngine identifier.
configobject, only if sentEcho of the clamped overrides actually applied.

5Config keys

Numeric keys only; anything else is ignored. Values are clamped to the ranges below. The defaults are the honesty contract — tighten freely, loosen knowingly (spec §5).

KeyDefaultClampMeaning
mergeSimilarity0.600.3 – 1Jaccard floor for merging two events (gate 1).
minSharedTokens31 – 10Shared-token floor for merging (gate 2).
confirmedAt42 – 20Origins needed for “confirmed”.
corroboratedAt22 – 20Origins needed for “developing”/“breaking”.
breakingWindowMs1 800 0001 min – 24 hPublish-age window for “breaking”.
receiptsCap61 – 20Max receipts per verdict (display cap; the count is never capped).
keyTokens83 – 16Tokens in the deterministic event key.

6Errors & limits

StatusBodyWhen
400{ "error": "claims must be a non-empty array" }Missing/empty claims.
400{ "error": "max 200 claims per call" }Batch too large — split it.
400{ "error": "payload too large (200 kB max)" } / { "error": "invalid JSON" }Body unreadable.
405{ "error": "POST a batch of claims" }Any method other than GET (usage doc) / POST / OPTIONS.

7Examples

curl -s https://sourced.run/api/assess \
  -H "content-type: application/json" \
  -d '{
    "claims": [
      {"id":"a","title":"Major dam breach reported upstream","origin":"reuters","publishedAt":"2026-07-10T21:00:00Z"},
      {"id":"b","title":"Major dam breach reported upstream","origin":"bbc","publishedAt":"2026-07-10T21:04:00Z"}
    ]
  }'
const res = await fetch("https://sourced.run/api/assess", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ claims }),
});
const { verdicts } = await res.json();
// verdicts[i] belongs to claims[i]
import requests

r = requests.post(
    "https://sourced.run/api/assess",
    json={"claims": [
        {"id": "a", "title": "Major dam breach reported upstream",
         "origin": "reuters", "publishedAt": "2026-07-10T21:00:00Z"},
        {"id": "b", "title": "Major dam breach reported upstream",
         "origin": "bbc", "publishedAt": "2026-07-10T21:04:00Z"},
    ]},
    timeout=10,
)
verdicts = r.json()["verdicts"]  # verdicts[i] belongs to claims[i]
# verdicts[1] -> {'corroboration': 2, 'corroboratingSources': ['reuters'], 'signal': 'breaking', ...}

8For AI agents (MCP)

Sourced ships as an MCP server — agents get the primitive as native tools: assess (with session memory: corroboration and first-seen accumulate across calls), verify_chain and run_conformance. The server identifies itself as sourced.

# Claude Code
claude mcp add sourced -- npx -y @sourcedhq/mcp

# any MCP client (config JSON)
{ "mcpServers": { "sourced": { "command": "npx", "args": ["-y", "@sourcedhq/mcp"] } } }

Machine-readable summaries of all three surfaces live at /llms.txt on each domain. This endpoint itself is agent-friendly by design: no auth, CORS open, JSON in/out, GET returns usage.