The Trust API
Build on the record. Become the trust layer downstream.
Colophon measures an object, verifies its material record against public government ledgers, and holds that record in a tamper-evident, publicly anchored chain. The Trust API packages that accountability, verification, and Material Integrity Index as endpoints your platform can call. Resolve an object once and the evidence travels with the piece wherever it changes hands.
Evidence infrastructure, not certification. Colophon supplies the verifiable record; you own whatever claim you build on it.
01 · Quickstart
Zero to a first call.
The read layer is public and CORS open, so your very first call needs no key and no account. Get a result now, then mint a key when you want a higher rate limit.
-
Call a public endpoint. No key, straight from a terminal or a browser.
This returns the live Material Integrity Index.
curl BASE/index
-
Sign in for a key. A key belongs to a Colophon account. Sign in with
your email to get a session (a magic link), which you carry as a bearer token.
curl -X POST BASE/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "you@studio.com"}' -
Mint a key. Call
POST /developer/keyswith your session. The full secret is returned exactly once.curl -X POST BASE/developer/keys \ -H "Authorization: Bearer YOUR_SESSION" \ -H "Content-Type: application/json" \ -d '{"label": "acme-prod"}' -
Send the key. Add
X-API-Keyto any read call to lift your per-minute limit. The response is identical; only your rate limit changes.curl BASE/index -H "X-API-Key: colophon_live_YOUR_KEY"
The base URL. Every example on this page is filled in with the live base for
wherever you are reading it. Served under the institutional site the base is
/colophon/api; served standalone the engine is the origin. Override
it explicitly by setting window.COLOPHON_API_BASE before this page's
script runs.
02 · Live console
Try it, right here.
Pick a public read endpoint, give it an id or hash where one is needed, and send. The call runs against the live base and prints the status and the JSON it returns.
These are the CORS-open GET endpoints, so the console calls them straight from your browser. The POST analysis endpoints below are server-side and are not called from here. If the engine is not reachable from where this page is served, the console says so plainly instead of faking a result.
03 · Endpoint reference
Every endpoint, one page.
Each endpoint carries its method, path, auth, parameters, a runnable curl and JavaScript
example, and a real (abbreviated) response. Anchors are stable, so any endpoint is linkable
by its #id.
Public read layer · no key required
-
Material Integrity Index
The public scoreboard: overall mean and median spectacle multiple (retail divided by estimated make cost), a fixed-band distribution, a per-category breakdown, and named exemplars. Privacy safe: no owner, no object name, no personal data. A valid
curlX-API-Keyonly raises your rate limit.JavaScriptcurl BASE/index
Example responseconst res = await fetch("BASE/index"); const data = await res.json(); console.log(data.overall.median_multiple);{ "as_of": "2026-07", "sample_size": 42, "overall": { "mean_multiple": 9.1, "median_multiple": 7.8, "distribution": [ { "band": "4-8x", "count": 15 } ] }, "by_category": [ { "category": "exotic-leather bags", "median_multiple": 12.4 } ], "exemplars": [ { "object": "…", "spectacle_multiple": null } ], "disclaimer_block": { "disclaimer": "Estimates from public trade and commodity data…" } } -
Exposer objects
The curated specimen set behind Under the Price: each object with its public retail price, estimated make cost, resulting multiple, material, and provenance. Handy for pulling a real object id to use elsewhere. A CSV of the same seed is at
curlGET /exposer/export.csv, and one object atGET /exposer/objects/{id}.JavaScriptcurl BASE/exposer/objects
Example responseconst res = await fetch("BASE/exposer/objects"); const { objects } = await res.json(); console.log(objects[0].id);{ "count": 12, "objects": [ { "id": "exotic-croc-tote-01", "object": "Exotic-leather top-handle bag", "retail_usd": 25000, "make_cost_usd": 6000, "spectacle_multiple": 4.2, "material": "CITES-listed crocodilian leather" } ], "disclaimer_block": { "disclaimer": "Evidence infrastructure, not a certification…" } } -
Standards conformance
The machine-readable account of which open standards Colophon implements (W3C Verifiable Credentials 2.0, did:web, EdDSA/Ed25519, GS1 Digital Link, and a DPP-aligned passport), plus a live self-check that issues a credential and verifies it against the published DID document at request time.
curlJavaScriptcurl BASE/standards/conformance
Example responseconst res = await fetch("BASE/standards/conformance"); const data = await res.json(); console.log(data.conformant, data.self_check.vc_roundtrip);{ "service": "colophon-engine", "standards": [ { "id": "w3c-vc-2.0", "spec": "https://www.w3.org/TR/vc-data-model-2.0/" } ], "self_check": { "vc_roundtrip": true, "did_doc_resolves": true, "signature_alg": "EdDSA" }, "conformant": true, "reference_implementation": { "did_document": "https://…/.well-known/did.json" } } -
Badge
One object's material record plus the evidence-not-certification disclaimer block. This is exactly what the embeddable widget renders. Registered atelier objects resolve first; the curated seed is the fallback.
object_idpath, required a registered object UUID, or a seed id such asexotic-croc-tote-01.
JavaScriptcurl BASE/badge/OBJECT_ID
Example responseconst res = await fetch("BASE/badge/" + encodeURIComponent(objectId)); if (res.ok) console.log((await res.json()).object.spectacle_multiple);{ "object": { "id": "…", "object": "Silk-twill scarf, 90cm", "retail_usd": 10000, "make_cost_usd": 1200, "spectacle_multiple": 4.5, "material": "woven mulberry silk twill" }, "disclaimer_block": { "disclaimer": "Evidence infrastructure, not a certification…" } } -
Verify a record hash
Is this exact material-record hash in the hash-chained ledger, and is it still internally consistent (untampered)? A positive result is evidence a record is independently verifiable and unaltered, never a guarantee of authenticity. The human-readable
messageappears only while the chain holds.record_hashpath, required the canonical SHA-256 record hash.
JavaScriptcurl BASE/verify/RECORD_HASH
Example responseconst res = await fetch("BASE/verify/" + encodeURIComponent(recordHash)); const data = await res.json(); console.log(data.verification.present, data.verification.chain_intact);{ "record_hash": "…", "verification": { "present": true, "chain_intact": true, "anchor": { "status": "anchored" } }, "message": "This record is anchored to a public blockchain and is independently verifiable…", "disclaimer_block": { "disclaimer": "…" } } -
Verify a registered object
Recompute a registered object's current material-record hash and return its ledger proof, so you can verify by object id without knowing the hash yourself.
object_idpath, required a registered object UUID.
JavaScriptcurl BASE/verify/object/OBJECT_ID
Example responseconst res = await fetch("BASE/verify/object/" + encodeURIComponent(objectId)); const data = await res.json(); console.log(data.record_hash, data.verification.chain_intact);{ "object_id": "…", "record_hash": "…", "verification": { "present": true, "chain_intact": true, "anchor": { "status": "pending" } }, "disclaimer_block": { "disclaimer": "…" } } -
Passport
The full package resolved once: a persistent identity (a did:web DID and a GS1 Digital Link), our accountability record (a signed Verifiable Credential plus its ledger proof), the brand's own authenticity reference if one is linked, and a Digital Product Passport aligned block. The DPP block alone is at
GET /passport/{object_id}/dpp.json.object_idpath, required a registered object UUID.
JavaScriptcurl BASE/passport/OBJECT_ID
Example responseconst res = await fetch("BASE/passport/" + encodeURIComponent(objectId)); const data = await res.json(); console.log(data.identity.did, data.accountability.ledger_proof.present);{ "object_id": "…", "identity": { "did": "did:web:…", "gs1_link": "https://…" }, "accountability": { "vc_jwt": "…", "ledger_proof": { "present": true } }, "authenticity": { "ref": null, "note": "No brand authenticity attestation linked yet…" }, "dpp": { "schema": "dpp-aligned/groundwork-0.2" }, "disclaimer_block": { "disclaimer": "…" } } -
Evidence package
A defensible, timestamped, independently verifiable bundle for one registered object: its Material Integrity Record, the record hash, the ledger proof (chain intact, anchor status, the honest integrity line), and the signed Verifiable Credential with the issuer DID and a pointer to the published DID document, so anyone can verify it without our database. This is what a litigator attaches or a brand keeps in its compliance file.
object_idpath, required a registered object UUID.
JavaScriptcurl BASE/evidence/package/OBJECT_ID
Example responseconst res = await fetch("BASE/evidence/package/" + encodeURIComponent(objectId)); const data = await res.json(); console.log(data.record_hash, data.verify_url);{ "object_id": "…", "generated_at": "2026-07-26T12:00:00Z", "material_integrity_record": { "object": "…", "retail_usd": 450, "spectacle_multiple": null }, "record_hash": "…", "ledger_proof": { "present": true, "chain_intact": true }, "credential": { "vc_jwt": "…", "issuer_did": "did:web:…", "did_document_url": "https://…" }, "verify_url": "https://…/verify/…", "disclaimer_block": { "disclaimer": "…" } }
Analysis endpoints · server-side POST
Not CORS-open. These endpoints run a fetch, an estimate, or a corpus reading
server-side, so call them from your own backend, not from a browser. The curl and Node
fetch examples show the exact request bodies.
-
Reading
Evidence on a single object: an estimated make cost, the Material-to-Market Ratio ($MMR), a grounded reading over the corpus, its citations, and the working behind every input. Anonymous callers get one instant headline figure; a signed-in account gets the full reading and three free full readings, then HTTP 402. It returns no recommendation. The
buy/hold/walk_awayfield was retired on 14 August 2026 and is no longer present in the response. We present evidence, not a verdict. Not financial advice.urlbody a product page to read, ortextbody a free-text description with a price, orobject,category,retail_usd,materialbody structured fields.
JavaScript (Node, server-side)curl -X POST BASE/verdict \ -H "Authorization: Bearer YOUR_SESSION" \ -H "Content-Type: application/json" \ -d '{"object": "Silk-twill scarf, 90cm", "retail_usd": 450, "material": "woven mulberry silk twill"}'Example responseconst res = await fetch("BASE/verdict", { method: "POST", headers: { "Authorization": "Bearer " + session, "Content-Type": "application/json" }, body: JSON.stringify({ object: "Silk-twill scarf, 90cm", retail_usd: 450, material: "silk" }) }); const data = await res.json(); console.log(data.spectacle_multiple, data.basis_source);{ "object": "Silk-twill scarf, 90cm", "spectacle_multiple": 4.5, "mmr": 4.5, "basis_source": "curated", "headline": "Estimated make-cost $100 against a $450 retail price: a Material-to-Market Ratio of 4.5x…", "true_worth": { "pay_usd": 450, "makes_usd": 100, "resells_usd": 180 }, "gate": { "unlocked": "full", "remaining": 2, "limit": 3 }, "as_of": "2026-07", "methodology_version": "1.1", "not_advice": "Evidence from public data. Not a recommendation…" } -
True worth
The lean three-number view: what you would pay (retail), what it plausibly costs to make (estimated make cost), and what it would resell for (estimated secondary-market value), each a clearly labelled estimate, plus the spectacle multiple and resale ratio. No funnel, no corpus call. Same intake as verdict.
url/text/ structuredobject, category, retail_usd, materialbody
JavaScriptcurl -X POST BASE/trueworth \ -H "Content-Type: application/json" \ -d '{"object": "Silk-twill scarf, 90cm", "retail_usd": 450, "material": "silk"}'Example responseconst res = await fetch("BASE/trueworth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ object: "Silk-twill scarf, 90cm", retail_usd: 450, material: "silk" }) }); console.log((await res.json()).true_worth);{ "object": "Silk-twill scarf, 90cm", "spectacle_multiple": 4.5, "true_worth": { "pay_usd": 600, "makes_usd": 120, "resells_usd": 210, "resale_ratio": 0.35 }, "make_cost": { "make_cost_usd": 120, "confidence": "medium" }, "disclaimer": "This is our reading; you own the decision." } -
Valuation
A neutral valuation reference, the first figure that does not work for the seller: replacement value (retail), market value (estimated resale), and material floor (estimated or recorded make cost), with a confidence band and use notes saying which figure suits insurance, resale, estate or tax, and collateral. An
object_idties the valuation to a verifiable record. An estimate from public data, not a certified appraisal.object_idbody a registered object, orurl/text/ structured fields body the same intake as verdict.
JavaScriptcurl -X POST BASE/valuation \ -H "Content-Type: application/json" \ -d '{"object": "Diamond tennis bracelet", "retail_usd": 18000, "material": "18k gold, diamond"}'Example responseconst res = await fetch("BASE/valuation", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ object_id: objectId }) }); const v = await res.json(); console.log(v.replacement_value, v.market_value, v.material_floor);{ "object": "Diamond tennis bracelet", "replacement_value": 18000, "market_value": 9000, "material_floor": 5200, "valuation_range": { "low_usd": 7200, "point_usd": 9000, "high_usd": 10800, "band": "+/-20%" }, "use_notes": { "insurance": "For insurance replacement, use the REPLACEMENT value ($18,000)…" }, "not_certified": "Not a certified appraisal…" } -
Greenwash scan
Point it at a marketing page or paste the copy; it flags the environmental claims restricted under the EU ECGT (Directive 2024/825) unless each is backed by verifiable, third-party evidence. A detector of legal risk and missing substantiation, not an accusation that any brand is lying.
textbody marketing copy to scan, orurlbody a page to fetch and scan (robots respected).
JavaScriptcurl -X POST BASE/greenwash \ -H "Content-Type: application/json" \ -d '{"text": "Our carbon-neutral, eco-friendly bag is 100% sustainable."}'Example responseconst res = await fetch("BASE/greenwash", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: "Our carbon-neutral, eco-friendly bag is 100% sustainable." }) }); console.log((await res.json()).flagged_count);{ "source": "text", "flagged_count": 3, "flagged": [ { "phrase": "carbon-neutral", "category": "offset_neutrality" } ], "categories": { "generic_environmental": 2, "offset_neutrality": 1, "self_declared_label": 0 }, "summary": "3 claims here are restricted under ECGT (effective 27 Sept 2026)…", "disclaimer": "A detector of legal risk and missing substantiation…" } -
Surveillance (batch)
Regulator and journalist ECGT surveillance at scale. Point it at a batch of brand pages or pasted copy (up to 25 sources); every source runs through the same deterministic scanner as
/greenwashand the flags aggregate into one enforcement-ready reading. A validX-API-Keyraises the rate limit.urlsbody an array of pages to fetch and scan, and/ortextsbody an array of marketing copy blocks.
JavaScriptcurl -X POST BASE/evidence/surveil \ -H "Content-Type: application/json" \ -d '{"texts": ["100% sustainable, carbon neutral", "eco-friendly and green"]}'Example responseconst res = await fetch("BASE/evidence/surveil", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ urls: ["https://brand.example/eco"] }) }); const data = await res.json(); console.log(data.total_restricted_claims, data.sources_scanned);{ "sources_scanned": 2, "sources_readable": 2, "total_restricted_claims": 5, "categories": { "generic_environmental": 3, "offset_neutrality": 2, "self_declared_label": 0 }, "sources": [ { "kind": "text", "ref": "text #1", "flagged_count": 3 } ], "disclaimer": "Colophon provides evidence, not legal advice…" }
Key management · bearer session required
-
Mint a key
Mint an API key for the signed-in account. The full secret is returned exactly once and is never stored, logged, or shown again. Only its hash and a visible prefix live in our database.
labelbody, optional a human name for the key, such asacme-prod.
JavaScriptcurl -X POST BASE/developer/keys \ -H "Authorization: Bearer YOUR_SESSION" \ -H "Content-Type: application/json" \ -d '{"label": "acme-prod"}'Example responseconst res = await fetch("BASE/developer/keys", { method: "POST", headers: { "Authorization": "Bearer " + session, "Content-Type": "application/json" }, body: JSON.stringify({ label: "acme-prod" }) }); const { key, key_prefix } = await res.json(); // store `key` now; shown once{ "id": "…", "key": "colophon_live_9f2c…", "key_prefix": "colophon_live_9f2c", "rate_per_min": 120, "note": "Store this key now. It is shown once and cannot be retrieved again." } -
List your keys
List the caller's own keys, prefixes only, never the secret. Revoked keys are included and flagged.
curlJavaScriptcurl BASE/developer/keys \ -H "Authorization: Bearer YOUR_SESSION"Example responseconst res = await fetch("BASE/developer/keys", { headers: { "Authorization": "Bearer " + session } }); console.log((await res.json()).keys);{ "count": 1, "keys": [ { "id": "…", "label": "acme-prod", "key_prefix": "colophon_live_9f2c", "rate_per_min": 120, "revoked": false } ] } -
Revoke a key
Revoke one of the caller's own keys (soft delete). A key that is not yours, already revoked, or absent returns 404.
idpath, required the key id from the list response.
JavaScriptcurl -X DELETE BASE/developer/keys/KEY_ID \ -H "Authorization: Bearer YOUR_SESSION"Example responseconst res = await fetch("BASE/developer/keys/" + keyId, { method: "DELETE", headers: { "Authorization": "Bearer " + session } }); console.log(res.status); // 200 revoked, 404 not found{ "id": "…", "revoked": true }
04 · Error reference
Every status, and what to do about it.
Errors return JSON with an error code and a human
message. The status line tells you what happened and what to change.
| Status | Meaning | What to do |
|---|---|---|
| 200 | OK | The call succeeded. Some read failures on the POST endpoints also return 200 with parsed: false and a message asking you to describe the item, rather than an error. |
| 400 | Bad input | The body is malformed or out of bounds (for example a surveillance batch over 25 sources, or an invalid authenticity ref). Read message and fix the request body. |
| 401 | Auth required | No valid session. Sign in with your email for a bearer token and send it as Authorization: Bearer …. The first anonymous verdict is the one call that returns 401 once its single free use is spent. |
| 402 | Quota exceeded | A free account has used its three full verdicts. Upgrade to Custodian for unlimited. The gate object reports used and limit. |
| 404 | Not found | No object, record, or key with that id. Public read 404s still carry the disclaimer block. Check the id, and note only registered objects have a passport or evidence package. |
| 429 | Rate limited | Over the per-minute limit. Slow down, honour Retry-After, or present a valid X-API-Key for your own higher bucket. Anonymous callers share the public per-IP default. |
| 503 | Not configured | A feature is not stood up on this instance (for example no issuer signing key, so no signed credential). Identity and ledger proof still resolve; retry later or use a fully configured deployment. |
Rate-limit headers. A 429 carries Retry-After in
seconds and a body with scope (public_default
or per_api_key) and a hint. Back off, then
retry.
05 · Auth & keys
Two credentials, one purpose.
A bearer session proves who you are (to mint keys and to unlock full verdicts). An API key raises your rate limit on the public read layer. The public reads work with neither; a key never changes what an endpoint returns.
- Sign in. An API key belongs to a Colophon account. Post your email to
/auth/requestand follow the magic link; the link hands your browser a session it stores locally. - Mint a key. Call
POST /developer/keyswith a label. The full secret is returned exactly once. Store it then, because it cannot be retrieved again. - Send it. Add
X-API-Key: your_keyto your read requests. List your keys withGET /developer/keysand revoke one withDELETE /developer/keys/{id}.
Mint one now
If you are signed in on this device, name a key and mint it here. The secret appears once, below.
Copy it now. Colophon stores only a hash of this key and can never show it again.
The embeddable widget
Drop one line on a product page and the badge renders the object's record inline, reading the public Badge endpoint. No build step, no framework.
<script src="https://colophon.objectsofaffectioncollection.com/badge.js"
data-object="OBJECT_ID" async></script>Colophon is evidence infrastructure, not a certification of compliance, authenticity, or sustainability. The record you receive is verifiable and dated; the claim you make on it is yours. A key authenticates your platform and sets your rate limit; it does not license Colophon to speak for you, and it never changes what an endpoint returns.
06 · Versioning & changelog
Additive and stable.
The API is additive: we add endpoints and fields, we do not remove or rename what is already
shipped, so a response your code reads today keeps working. Read the running version from
GET /version.
-
Version
The running API version and the providers this deployment is wired to. Read it at startup to log which build you are talking to.
curlExample responsecurl BASE/version
{ "service": "colophon-api", "version": "0.4.0", "embeddings_provider": "…", "llm_provider": "…" }
-
v0.4.0 · current
Passport and standards conformance, the DPP-aligned block, the evidence package and batch surveillance, and the neutral valuation reference. Developer key management under
/developer/keys. -
v0.3.x
The verdict funnel with true-worth, greenwash detection, and the public Material Integrity Index.
-
v0.2.x
The public read layer: badge, verify, and the Under the Price seed, all CORS-open.