Reference · Contract 2 · Public reads · No key
Company records your code can read.
Every public fact on Trooth is structured and source-labeled. Witnessed facts carry a read time and signature; an unrecorded date reads as unknown, never borrowed.
- 01Understand what you can buildWhat the interface answers, what it never answers, and where each address points.
- 02Make your first working callFive steps from nothing to a profile read that branches correctly.
- 03Judge whether you can depend on itLimits as numbers, versioning and deadlines, and what to check before production.
Introduction
The Trooth application programming interface (API) answers questions about software and AI companies from published records. Every endpoint in this reference is a read over an encrypted connection, in JavaScript Object Notation (JSON), with no key and no account. Public calls that change state, such as subscribing an endpoint to a company's changes, are named in the interface catalog with their side effects.
Each answer carries its own limits:
- Where a fact came from.
- When Trooth last read it.
- What Trooth did not read.
No response contains a rating, grade or rank, and the schema refuses one.
What you can build
- A vendor check inside your own toolResolve a domain to its Trust Profile: witnessed, declared and unknown, each labeled.
- Search over the NetworkSearch or typeahead over the public directory's companies, paged by cursor.
- An agent that cites its sourcesConnect an assistant to the Model Context Protocol server so it reads sourced, dated records instead of guessing.
- An alert when a posture changesGet a signed event when what Trooth witnessed about a published profile changes.
What is not offered
There is no write endpoint, test environment, software development kit (SDK) or credentialed public API; alternatives are given below.
| Address | What it serves |
|---|---|
https://trooth.co | Every endpoint in this reference |
https://api.trooth.co/public/mcp | The Model Context Protocol server |
https://api.trooth.co/public/trust/:slug | Older read for existing clients; Network companies answer moved, naming the supported lookup. Contract: /openapi.yaml |
# Nothing to install.curl --versionnpm install -g troothTwo APIs, two kinds of access
Reading the Network needs no account. Writing into your own workspace needs a key bound to your client. They have different addresses, credentials and contracts; every operation below says which track it is on.
| Aspect | Public Network API | Workspace API |
|---|---|---|
| For | Anyone reading a company's public record: buyers, tools and AI agents. | A company's own workspace, sending evidence events into it. |
| Base address | https://trooth.co; the canonical read is GET /api/network/profile. The MCP server is https://api.trooth.co/public/mcp, also keyless. | https://api.trooth.co |
| Credential | None: no key, no account, no cookie. | A workspace API key (tk_live_) bound to your client's key with DPoP (RFC 9449), sent with a new proof on every request. A key created without that binding is an ingest key that reaches only POST /v1/evidence/webhook. |
| Scope | Published records only; nothing private is reachable. | One workspace: a key acts only for the workspace whose member created it. |
| Lifecycle | Pin ?contract=2; a breaking change moves the number, with 90 days' notice under the versioning policy. | A key is created by a member of the workspace, shown once and stored only as a hash; revocation takes effect on the next request. |
| Limits | Per client network, stated on each operation; the MCP server meters units per caller. | Not published per route today; the authorization model says what each credential can reach. |
| Contract | openapi.json and the contract 2 schema. | openapi.yaml and the authorization model. |
| Start here | Quickstart: one GET, no setup. | API keys: create a bound key in your workspace. |
Authentication
The public application programming interface (API) needs none: every endpoint reads published records.
Trooth's authenticated endpoints run the workspaces, are session scoped and are deliberately not public: a documented route map helps attackers and promises unsupported integrations. A credentialed API, if built, will be published here with scopes.
# No key, no account, no header required.curl "https://trooth.co/api/network/profile?q=example.com"Trooth-Contract: 2Live today
Listed means live now. Anything not listed is not live; Trooth does not advertise unshipped endpoints.
- Public Trust Profile JSONWitnessed state and the date it was last read, for any listed company.
- Embeddable witnessed badgeA company's live record on its own site in one line.
- Webhooks for driftPosture changes push to your tools.
- OpenAPI 3.1 contractServed at /openapi.json, generated from the routes and checked on every build.
- Trooth CLInpx trooth check <domain> reads a company's record on the Trooth Network from the terminal, and trooth lint reads what your own repository declares, locally. The package is trooth on npm. No account required.
- trooth-openapi on GitHubThe OpenAPI 3.1 contracts for both public hosts. Apache 2.0.
- trooth-mcp on GitHubThe read-only Model Context Protocol (MCP) connector: its server.json and setup for Claude, ChatGPT and Cursor. MIT.
- trooth-cli on GitHubThe source of the trooth package on npm. Apache 2.0.
npx trooth check trooth.coYour first working call, in about five minutes
Quickstart
Check the prerequisites
Any client that speaks an encrypted connection; the samples need Node.js 18+ or Python 3.8+. No account, no key.
Read a Trust Profile
Send
GET /api/network/profilewith a domain inq;q=trooth.coalways has an answer.Branch on found, not on the status alone
An unknown company is
200with{ "found": false }, an answer. A503means Trooth could not read its own record and says nothing about the company.Read each fact with its origin
Every entry in
factscarriesorigin: company-declared, Trooth-witnessed or public. Show it beside the value.Pin the contract
Send
Trooth-Contract: 2so no change of meaning arrives unannounced. The response names the contract that served it in the same header.
That works. Next, page with the cursor, follow changes, and read the production checklist.
curl -i "https://trooth.co/api/network/profile?q=trooth.co" \ -H "Trooth-Contract: 2"Errors
Errors are JSON with an error field; the status code carries the meaning:
400: the request was malformed. The body names the field.404: the Network holds no such record. An answer, not a failure.429: a rate limit. HonorRetry-Afterwhen present; otherwise pause and retry once.502and503: Trooth could not read its upstream, which says nothing about the company. Retry with a bound.5xx: ours. Retry with the same bound.
Problem details, and the request id
Every API response has an X-Request-Id: send your own (20 to 200 letters, digits, + / = -) or get one. Quote it when reporting failures; a W3C traceparent you send is continued.
Send Accept: application/problem+json to /api/network/profile or /api/network/profile/history for RFC 9457 problem documents; otherwise the body is unchanged.
The document carries:
code, stable, andtype, which links to that code below.retryable, withretry_afterandretry_atwhen a wait is known.next_action.errors, naming the parameter or field.request_id, the same value as the header.
invalid_request400The request could not be read as sent. Next:fix_request.missing_parameter400A required parameter is missing. Next:fix_request.unsupported_contract_version400That contract version is not served. Next:fix_request.invalid_cursor400That page marker cannot be used. Next:start_over.cursor_expired400That page marker has expired. Next:start_over.not_found404No published record answers to that identifier. Next:none.version_not_found404That record version is not published. Next:none.beyond_retention410That position is older than the change history keeps. Next:resync.rate_limited429Too many requests. Next:retry_later.busy429A request like this one is still running. Next:retry_later.capacity_saturated503Trooth is running as many of these as it allows at once. Next:retry_later.
Retries, and where they stop
Three rules. The third is the one most often left out.
- Retry only the classes that can change. That is
429,502,503and other5xx, never400or404. On a refused cursor, restart from page one, once. - Count the attempts and stop. Back off, cap attempts and pages, and surface the failure.
- An outage is not a finding. A
503does not mean no record; keep the last good answer or show the failed read.
| Code | Meaning | Retry |
|---|---|---|
| 200 | The answer. On a profile read it can be { found: false }. | No |
| 400 | The request was malformed. The body names the field. | No |
| 404 | The Network holds no such record. An answer. | No |
| 429 | A rate limit. Honor Retry-After when present. | Yes, bounded |
| 502 | No company in the request could be read. | Yes, bounded |
| 503 | Trooth could not read its own record. Says nothing about the company. | Yes, bounded |
{ "error": "profile_read_unavailable", "message": "The published profile record could not be read on this load. That is not the same as this company having published none, so no answer is given."}const RETRYABLE = new Set([429, 502, 503]);async function read(url, attempts = 4) { for (let i = 0; i < attempts; i++) { const res = await fetch(url); if (res.ok) return res.json(); // 400 and 404 are answers. Sending them again gets the same answer. if (!RETRYABLE.has(res.status) && res.status < 500) return { status: res.status, body: await res.json() }; const header = Number(res.headers.get("Retry-After")); const wait = Number.isFinite(header) && header > 0 ? header * 1000 : 500 * 2 ** i; await new Promise((r) => setTimeout(r, wait)); } // An outage is not a finding: surface it, do not record "no record". throw new Error("Trooth could not be read. Nothing is known about the company from this.");}Rate limits
Per IP address, per minute and per hour; over either, 429 with a plain JSON message.
The record lookup allows 600 requests a minute and 20,000 an hour per client network; its history has its own budget.
Read the header before waiting. Whether Retry-After is sent depends on the route, not the status:
GET /api/public-mesh/{handle}sends one with its503.- Some credentialed workspace routes send one with their
429.
If present, wait that long; if absent, back off and cap attempts (see Errors).
Other reads are edge-cached:
- Directory and typeahead: a sixty second edge cache.
- Company records: one hundred and twenty seconds.
- A legal document: an hour.
| Interface | Call | Access | Side effects | Availability | Support |
|---|---|---|---|---|---|
| Search the directory | GET trooth.co/api/mobile/directory/search | Public read | None: a read | Available | supported |
| Featured companies | GET trooth.co/api/mobile/directory/featured | Public read | None: a read | Available | supported |
| One directory entry | GET trooth.co/api/mobile/directory/vendor/{slug} | Public read | None: a read | Available | supported |
| A company's public rollup | GET trooth.co/api/public-mesh/{handle} | Public read | None: a read | Available | supported |
| A company's Trust Profile (the canonical lookup) | GET trooth.co/api/network/profile | Public read | None: a read | Available | supported |
| A company record's published versions: the change feed, and a read of one named version | GET trooth.co/api/network/profile/history | Public read | None: a read | Available | documented, not versioned |
| The section map: which sections each machine projection carries, leaves out, or cannot carry yet | GET trooth.co/section-capabilities.json | Public read | None: a read | Available | documented, not versioned |
| Name suggestions | GET trooth.co/api/network/suggest | Public read | None: a read | Available | supported |
| Multi-criteria discovery | GET trooth.co/api/network/search | Public read | None: a read | Available | supported |
| Batch read | POST trooth.co/api/network/batch | Public read | None: a read | Available | supported |
| Network change feed | GET trooth.co/api/network/changes | Public read | None: a read | Available | supported |
| Network change stream (Server-Sent Events) | GET trooth.co/api/network/changes/stream | Public read | None: a read | Available | supported |
| Error code registry | GET trooth.co/api/problems | Public read | None: a read | Available | supported |
| A legal document | GET trooth.co/api/legal/{slug} | Public read | None: a read | Available | supported |
| The deployed version | GET trooth.co/api/version | Public read | None: a read | Available | supported |
| A Trust Profile by workspace slug (older worker route) | GET api.trooth.co/public/trust/{slug} | Public read | None: a read | Available | deprecated, with a sunset date |
| Public signing keys | GET api.trooth.co/public/keys | Public read | None: a read | Available | supported |
| A company's badge image | GET api.trooth.co/public/trust/{slug}/badge.svg | Public read | None: a read | Available | supported |
| The embeddable badge script | GET trooth.co/badge.js | Public read | None: a read | Available | supported |
| Record a profile view | POST api.trooth.co/public/trust/{slug}/view | Public, changes state | records a view | Available | documented, not versioned |
| Subscribe an endpoint to a company's changes | POST api.trooth.co/public/trust/{slug}/agent-subscribe | Public, changes state | stores a subscription and sends requests later | Available | supported |
| Subscribe an address to a company's changes | POST api.trooth.co/public/trust/{slug}/subscribe | Public, changes state | stores a subscription and sends requests later | Available | documented, not versioned |
| Ask a company for a document | POST api.trooth.co/public/trust/{slug}/request-access | Public, changes state | stores a request | Available | documented, not versioned |
| MCP tool trooth_public_trust_profile: a company's complete Trust Profile, section by section, with a short summary and a link to the full record | POST api.trooth.co/public/mcp trooth_public_trust_profile | Public read | None: a read | Available | supported |
| MCP tool trooth_ai_data_use_disclosures: a company's published AI and data-use declarations | POST api.trooth.co/public/mcp trooth_ai_data_use_disclosures | Public read | None: a read | Available | supported |
| MCP tool trooth_search_companies: search the companies published on the Trooth Network | POST api.trooth.co/public/mcp trooth_search_companies | Public read | None: a read | Available | supported |
| MCP tool trooth_outside_in_read: a live read of a domain's public surface | POST api.trooth.co/public/mcp trooth_outside_in_read | Public read | requests the named site once | Available | supported |
| MCP tool trooth_verify: check the signatures on a Trust Ledger Token | POST api.trooth.co/public/mcp trooth_verify | Public read | None: a read | Available | supported |
| MCP tool trooth_ask: a question about Trooth itself | POST api.trooth.co/public/mcp trooth_ask | Public read | None: a read | Available | supported |
| MCP tool trooth_resolve_company: find a company's published record from a domain, web address, slug or name | POST api.trooth.co/public/mcp trooth_resolve_company | Public read | None: a read | Available | supported |
| MCP tool trooth_get_fact: one fact or buyer question answer from a company's record | POST api.trooth.co/public/mcp trooth_get_fact | Public read | None: a read | Available | supported |
| MCP tool trooth_list_companies: list the companies published on the Trooth Network by published facts | POST api.trooth.co/public/mcp trooth_list_companies | Public read | None: a read | Available | supported |
| MCP tool trooth_company_changes: how a company's record changed | POST api.trooth.co/public/mcp trooth_company_changes | Public read | None: a read | Available | supported |
| MCP tool trooth_my_company_record: your own workspace's company record | POST api.trooth.co/public/mcp trooth_my_company_record | Private read, sign-in | None: a read | Available | documented, not versioned |
| MCP tool trooth_profile_get: your own workspace's Trust Profile draft and its revision | POST api.trooth.co/public/mcp trooth_profile_get | Private read, sign-in | None: a read | Available | supported |
| MCP tool trooth_profile_checklist: what is left before your Trust Profile is published, and how to do it | POST api.trooth.co/public/mcp trooth_profile_checklist | Private read, sign-in | None: a read | Available | supported |
| MCP tool trooth_profile_update: change fields of your own Trust Profile draft, each value with its source | POST api.trooth.co/public/mcp trooth_profile_update | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| MCP tool trooth_profile_publish: publish your own Trust Profile draft so anyone can read it | POST api.trooth.co/public/mcp trooth_profile_publish | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| MCP tool trooth_profile_domain_start: start proving your company's domain, and get its DNS TXT record | POST api.trooth.co/public/mcp trooth_profile_domain_start | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| MCP tool trooth_profile_domain_check: check your company's domain proof in DNS now | POST api.trooth.co/public/mcp trooth_profile_domain_check | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| The A2A Agent Card: this agent's interface, not any company's record | GET api.trooth.co/.well-known/agent-card.json | Public read | None: a read | Available | supported |
| The A2A agent: three read-only tasks over A2A 1.0, JSON-RPC binding | POST api.trooth.co/a2a/v1 SendMessage: company_record, verify_token, ask | Public read | None: a read | Available | supported |
| A2A task retention: GetTask, ListTasks and CancelTask on a task only your token can read back | POST api.trooth.co/a2a/v1 GetTask, ListTasks, CancelTask | Private read, sign-in | None: a read | Available | documented, not versioned |
| Browser tools on every public page: search the Network, read a Trust Profile, read this page | WebMCP trooth.co trooth_search_network, trooth_read_trust_profile, trooth_read_this_page | Public read | None: a read | Available | documented, not versioned |
| Browser tools in a signed-in workspace: read, fill, save and publish the company's own profile | WebMCP trooth.co trooth_profile_read, trooth_profile_field_catalog, trooth_profile_update_fields, trooth_profile_save_draft, trooth_profile_publish | Browser publication, signed in | saves or publishes the company's profile | Available | documented, not versioned |
| Read your own Trust Profile (agent) | GET trooth.co/api/v1/profile | Private read, sign-in | None: a read | Available | supported |
| Change your own Trust Profile (agent) | PATCH trooth.co/api/v1/profile | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| Publish your own Trust Profile (agent) | POST trooth.co/api/v1/profile/publish | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| Agent API: prove the company's domain (POST starts it and answers the TXT record, PUT checks it) | POST trooth.co/api/v1/profile/domain | Changes the company's own profile, with its owner's agent key | saves or publishes the company's profile | Available | supported |
| The trooth command line | npx trooth check, profile, lint, verify, log, mirror, public-record, mcp-tools, guard, declare, evidence verify, delegation verify | Public read | None: a read | Available | supported |
| Any public page as Markdown | GET trooth.co/{page}.md, or Accept: text/markdown | Public read | None: a read | Available | documented, not versioned |
| Surface | Limit | Over it |
|---|---|---|
GET /api/network/profile | 600 a minute and 20,000 an hour per client network (an IPv4 address or an IPv6 /64), shared across instances | 429 with Retry-After: the seconds until that window resets |
GET /api/network/profile/history | 120 a minute and 3,000 an hour per client network (an IPv4 address or an IPv6 /64), shared across instances | 429 with Retry-After: the seconds until that window resets |
| Every other read on trooth.co | No per-route limit. Served from an edge cache. | Not applicable |
GET api.trooth.co/public/trust/:slug | None | Not applicable |
| MCP tools/call | 300 units every 60 seconds per caller (OAuth subject, otherwise IP address), shared across every server instance; a tool call costs 1 to 4 | 429 with Retry-After giving the seconds until units free up |
Pagination
Use the cursor. Send limit, then each returned next_cursor as cursor until next_cursor is null. An unreadable cursor answers 400, not an empty page.
The order, and the tie-break
The cursor carries the sort key, four fields in order:
- Whether the company is witnessed.
- When it was last witnessed, most recent first.
- The lowercased name.
- The company's own identifier.
The identifier breaks ties between companies that share a name.
So every row has one position. The key is in lib/directory-rank.ts; the comparator derives from it.
What the cursor prevents
It prevents the offset failure. With offsets, companies listed mid-walk shift rows, so you silently get some twice and miss others.
A cursor names a position, so inserted rows do not shift it.
What it does not prevent
It is not a snapshot, and there is nothing to pin. Each request reads the live directory; there is no snapshot id, as-of parameter or version boundary.
Two sort-key fields move (whether and when a company was witnessed), with two unmarked outcomes:
- An omission. A company ahead of you is witnessed mid-walk; its key moves behind your cursor and you never see it.
- A duplicate. A company you received stops being witnessed mid-walk; its key moves ahead and you get it twice.
No cursor prevents either; a walk is best-effort discovery over a list that changes, not a consistent view of the Network.
Deduplicating, and resynchronizing
Drop repeats by slug. The omission is undetectable within one walk:
- For a complete set, walk twice and reconcile.
- Keep walks short, so the list moves less.
- For a boundary, use the rows'
last_scan, not your request time.
The marker always advances, so a walk cannot loop, but pages are unbounded: rows added ahead of you are served too.
Cap the pages, never resend a marker, and stop on a null next_cursor, not an empty page.
Expiry, and what the cursor is not bound to
The cursor does not expire. It holds a payload version and a position, no issue time, so old markers still work.
These get a 400 naming the reason, never a silent reset to page one:
- A marker from an older payload version.
- A marker that cannot be decoded.
- A marker issued to a workspace-scoped list, used on a public one.
The cursor is not bound to your query. q and industry are not in it, so another filter returns that filter's rows after the old position, like a short page.
Keep the filter beside the cursor. total is per request and changes between pages; do not count up to it.
The cursor is opaque and belongs to this list; do not parse, build or reuse it elsewhere, where it is refused.
The offset alias, and how to migrate off it
offset and next_offset still work for clients that shipped with them. They are deprecated:
- Responses paged by
offsetcarryDeprecationandLink(rel="deprecation") headers pointing here. - No withdrawal date is set, so no
Sunsetheader is sent; the changelog would announce one first.
Migrating takes one step. Send the offset path's next_cursor as cursor and drop offset; with both, the cursor wins.
# First pagecurl "https://trooth.co/api/mobile/directory/search?q=payments&limit=100"# Every next page: send next_cursor back as cursor,# and stop when next_cursor is nullcurl "https://trooth.co/api/mobile/directory/search?q=payments&limit=100&cursor=NEXT_CURSOR"Idempotency
Every public endpoint documented here is a read, POST /api/network/batch included, so a repeat is always safe and needs no key.
Writes take one. Signed-in writes on trooth.co (publishing a profile, a domain claim, an invitation) accept an Idempotency-Key header of 8 to 255 characters, kept for 24 hours: the same key with the same request returns the stored answer, the same key with a different request is refused with 409, and the Idempotency-Status response header says which happened. Writes to api.trooth.co follow its own rule, set out under Authorization in the API documentation, where a reused key with a different request is 422.
GET /api/legal/{slug}GET /api/mobile/directory/featuredGET /api/mobile/directory/searchGET /api/mobile/directory/vendor/{slug}GET /api/network/changesGET /api/network/changes/streamGET /api/network/profileGET /api/network/searchGET /api/network/suggestGET /api/problemsGET /api/public-mesh/{handle}GET /api/versionPOST /api/network/batchVersioning
The profile read is versioned: 2 is current; 1 and 2 are answered. Pick one with the Trooth-Contract header or contract parameter; send neither for the current one.
- Both, disagreeing, is refused with a
400. - An unsupported version is refused with a
400; nothing is approximated. - Every answer names its contract in the
Trooth-Contractheader andcontractVersion, and the one you asked for inrequestedContract. - What moves the number: a removed or renamed field, or a changed meaning. Added fields do not; ignore unknown fields.
Deprecations and deadlines
Version 1 is withdrawn on 2027-03-01. It promises one answer per question, which holds only where no two sources disagree, so this version 1 response leaves out every fact two sources answered differently and names each in `withheldContested`; version 2 publishes them with every account in `conflicts`. Move to 2 and read `conflicts`. Until then, version 1 is still served, with Deprecation and dated Sunset headers.
Policy: API versioning. Each deprecation and deadline is announced first in the changelog.
# In a headercurl "https://trooth.co/api/network/profile?q=trooth.co" \ -H "Trooth-Contract: 2"# Or in the querycurl "https://trooth.co/api/network/profile?q=trooth.co&contract=2"{ "found": false, "error": "unsupported_contract_version", "message": "This deployment answers contract versions 1, 2 and was asked for 9. ...", "contractVersion": 2}Testing
There is one environment, production, and no test mode. Reads change nothing and cost nothing.
Production cannot put a company into a state on request. Two states are reliable:
- A
.invaliddomain, which can never be registered, returnsfound: false. - A missing
qreturns400.
For a failed read, a company with no observation and a contested fact, write fixtures from the schema; you will not meet these by chance.
Validate responses against the schema. The route is tested against it every push and deployment, so a failure is worth reporting.
# The schema the route is tested against on every pushcurl -O "https://trooth.co/schemas/network-profile.v2.schema.json"| State | How to reach it |
|---|---|
| found: true | q=trooth.co |
| found: false | q=nothing-here.invalid |
| 400 | no q, or contract=9 |
| 503 | A fixture. It cannot be requested. |
| standing: null | A fixture built from the schema. |
| A contested fact | A fixture built from the schema. |
The data objects
Four stable objects underpin every Trooth surface. Fields are added, never silently changed.
- Company record
- Identity, products and witnessed posture for one company: stable slug, domains and last-witnessed state.
- Evidence receipt
- One signed reading (source, scope, timestamp, signature), the base unit.
- Provenance label
- A closed vocabulary for where a fact came from: Trooth witnessed, Re-read on a schedule, Integration supplied, Independent third-party, Declared, Public record, Customer reported, Disputed, Stale, Unknown.
- Freshness stamp
- Last witnessed date, expected cadence and stale behavior, to weigh a fact by age.
# Public read. No key, no account.curl "https://trooth.co/api/network/profile?q=your-co.com"# The published record. Abridged: the response also carries contractNote,# contractOmissions, deprecation and methodology, and the full field list is# https://trooth.co/schemas/network-profile.v2.schema.json{ "found": true, "contractVersion": 2, "contractSchema": "https://trooth.co/schemas/network-profile.v2.schema.json", "slug": "your-co", "name": "Your Co", "domain": "your-co.com", "canonicalUrl": "https://trooth.co/network/company/your-co", "tagline": "Payments infrastructure for marketplaces", "industries": ["Payments"], "updatedAt": "2026-08-22T14:03:11Z", "witnessed": { "standing": "witnessed", "lastWitnessed": "2026-08-22T14:03:11Z", "firstWitnessedAt": "2026-06-01T09:12:40Z", "coverage": { "checksPassed": 11, "checksRun": 12 }, "continuity": { "recordBegan": "2026-06-01T09:12:40Z", "unbrokenSince": "2026-07-04T02:00:00Z", "unbrokenReadings": 1176, "totalReadings": 1904 } }, "facts": [ { "key": "hosting.production-regions", "category": "Hosting", "label": "Production regions", "value": "us-east-1, eu-west-1", "origin": "company-declared" } ], "conflicts": []}{ "key": "transport.tls-version", "category": "Transport", "label": "TLS version", "value": "TLS 1.3", "origin": "witnessed", "contested": false}{ "receipt_id": "rcpt_01J8Z9M4T6Q2", "subject": "your-co.com", "source": "trooth-witness-scan", "scope": "transport, dns, disclosure, supply-chain, app-sec", "observed_at": "2026-08-22T14:03:11Z", "freshness": { "category": "security", "stale_at": "2026-08-24T14:03:11Z" }, "signature": { "alg": "ed25519", "key_id": "trooth-2026-08" }}{ "id": "evt_a1b2c3d4e5f6", "type": "witness.changed", "created": 1785340800000, "data": { "coverage": { "passed": 41, "run": 44 }, "previous": { "passed": 40, "run": 44 }, "change": { "passed": 1, "run": 0 }, "source": "capability", "changed_at": 1785340800000 }}Reading a record correctly
Every material fact carries its source. Three rules:
- Unknown is never no. An absent fact is no evidence either way, not a failing control.
- Stale is not the same as wrong. A fact past its refresh window keeps its value; weigh it by the record's last reading, since facts carry no date.
- Declared is not witnessed. Keep the two labels distinct wherever you show them.
The full vocabulary and how each state is reached is in the methodology.
One fact, in full
One entry from facts[] on GET /api/network/profile, a Trust Profile's unit:
{ "key": "hosting.production-regions", "category": "Hosting", "label": "Production regions", "value": "us-east-1, eu-west-1", "origin": "company-declared" }key is stable and category-scoped: Region under hosting and Region under registration are different questions.
origin is company-declared, witnessed or public-source. Blank values are not published, so unanswered reads as unanswered.
Disagreeing sources mark a fact contested, with every account under its key in top-level conflicts:
"conflicts": [ { "key": "hosting.production-regions", "category": "Hosting", "label": "Production regions", "accounts": [ { "origin": "company-declared", "value": "eu-west-1" }, { "origin": "witnessed", "value": "us-east-1, eu-west-1" } ] } ]On a contested fact, value is one account, not the answer. There is no winner field or preferred source; accounts follow a fixed origin order, not a ranking.
Five dimensions, and they are separate fields
Collapsing any two produces a wrong sentence about a real company.
- Provenance is
facts[].origin, always present: a witnessed reading or a company's answer. - Availability has three shapes:
found: false(no record), a503(read failed, asserts nothing) andwitnessed.standing: null(no observation on the last reading). Collapse them and an outage becomes a finding. - Freshness is per fact:
facts[].freshness_windowandfacts[].stale_after, from the category windows on the methodology page, both null for a company statement Trooth does not re-read. The record contract (identity, record_summary, buyer_questions, controls_not_in_place, and per fact fact_id, anchor_url, sentence, witnessed_value and edit_history) is described in the published schema, https://trooth.co/schemas/network-profile.v2.schema.json. - Dispute is
contestedandconflicts, shown above. Uncontested means nobody answered differently, not corroborated. - Integrity covers one field; the response and a
200are unsigned. If the reading behindwitnessedwas signed,witnessStatementholds the signed string inpayload(facts, no verdict) and an Ed25519signature, checked with thekey_idkey at verify/keys.Older readings have no statement; all other fields are unsigned.
Each response lists in contractOmissions what the contract deliberately leaves out:
- No confidence number, per fact or overall.
- No adjudication between disagreeing sources.
- No completeness signal.
- No merging of two wordings that look equivalent.
Contract version, and the alias still answered
Every response carries contractVersion (2 today). It moves only for a removed or renamed field or a changed meaning, never an added field.
Still answered: 1 and 2. Pin one; stop on unexpected values.
Version 1 is withdrawn on 2027-03-01. It promises one answer per question, which holds only where no two sources disagree, so this version 1 response leaves out every fact two sources answered differently and names each in `withheldContested`; version 2 publishes them with every account in `conflicts`. Move to 2 and read `conflicts`. Until then, each version 1 response repeats this in deprecation.
| Dimension | Read it from |
|---|---|
| Provenance | facts[].origin |
| Availability | found, a 503, witnessed.standing |
| Freshness | witnessed.lastWitnessed, methodology.freshness |
| Dispute | facts[].contested, conflicts |
| Integrity | witnessStatement |
Endpoints
Thirteen operations, all credential-free reads of published data. The contract, /openapi.json (OpenAPI 3.1), is generated from source and checked every build.
GET /api/legal/{slug}A published legal documentGET /api/mobile/directory/featuredCompanies with a witnessed recordGET /api/mobile/directory/searchSearch the Trooth NetworkGET /api/mobile/directory/vendor/{slug}One company recordPOST /api/network/batchCheck many companies in one callGET /api/network/changesEvery record version across the NetworkGET /api/network/changes/streamEvery record version, as Server-Sent EventsGET /api/network/profileOne company's canonical profile and witness factsGET /api/network/searchDiscover companies by several criteriaGET /api/network/suggestTypeahead over published companiesGET /api/problemsEvery error code and whether retrying can helpGET /api/public-mesh/{handle}A company's public evidence rollupGET /api/versionThe deployment currently serving
Each operation below lists its parameters, its answers and a request you can copy.
https://trooth.coGET /api/legal/{slug}GET /api/mobile/directory/featuredGET /api/mobile/directory/searchGET /api/mobile/directory/vendor/{slug}GET /api/network/changesGET /api/network/changes/streamGET /api/network/profileGET /api/network/searchGET /api/network/suggestGET /api/problemsGET /api/public-mesh/{handle}GET /api/versionPOST /api/network/batchGET /api/network/profile
Retrieve a Trust Profile
One company's canonical profile and witness facts.
Public Network API, no key · the canonical company read
The canonical published profile for a company, keyed by domain or slug, with the witness facts a buyer's tool needs: the standing field (the witnessed state), lastWitnessed (freshness), firstWitnessedAt and the unbroken run of Trooth's own hourly readings (continuity), and checksPassed out of checksRun on the signed result (coverage: checks that read as expected, out of checks run). Continuity is never claimed older than Trooth's own record and stays null until two consecutive readings agree. Responds { found: false } with 200 when no profile is published, 400 when q is missing. Cached at the edge for 120 seconds. Every response carries a methodology object stating the reading cadence, the gap tolerance that defines an unbroken series, the retention period, the per-category freshness windows, and an explicit list of coverage limits - what Trooth does not read. A caller restating witnessed or updatedAt elsewhere must carry those limits with it: neither field is a certification nor an audit opinion. Only the witness statement is signed; the profile, its facts and the company's text are not (see signing). sectionCapabilities links the section map, which says which sections this record and the other projections carry. Limited to 600 requests a minute and 20,000 an hour per client network (an IPv4 address, or an IPv6 /64), counted across server instances; over either window the answer is 429 with Retry-After set to the seconds until that window resets. If the counting store fails, each instance counts alone at the same ceilings and the read is not refused. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- qstring, in queryRequired
- Company domain (example.com) or Trooth slug.
- contractstring, in query
- Pin the response contract version, here or in the Trooth-Contract request header (not both with different values, which is refused with 400). Omit for the current contract. A version this deployment does not answer is refused with 400 rather than approximated. Every response names the contract that served it in the Trooth-Contract response header and in contractVersion, and the one requested in requestedContract; a request for contract 1 is served contract 1 itself, with Deprecation and Sunset headers, until its withdrawal on 2027-03-01, leaving out every fact two sources answered differently and naming each in withheldContested.
- sectionsstring, in query
- Comma-separated categories to return facts from: identity, registration, hosting, pricing, pricing-add-ons, security, privacy, privacy-roles, recovery, ai-practices. Omit for all. An unknown name is refused with 400 and the valid list; a section's conflicts travel with its facts.
- limitstring, in query
- Facts per page, 1 to 200. With limit or after, a page carries up to 100 facts unless limit says otherwise; with neither, the response carries every fact in the selected sections. The response's selection object says how many facts the selected sections hold, how many this page carries and whether it was truncated.
- afterstring, in query
- The selection.next cursor from the previous page. It is bound to the record version and to the sections asked for: if the company publishes a new version between pages, the cursor is refused with 400 (stale_cursor) rather than served from a different record. A cursor also expires 24 hours after the page that issued it and is then refused with 400 (cursor_expired).
Returns
- 200
- The record, or { found: false } when the Network holds none. Which body is served is keyed by the Trooth-Contract response header: 2 (the default) is NetworkProfileV2, written to the published JSON Schema https://trooth.co/schemas/network-profile.v2.schema.json and validated against it on every push and after every deployment; 1 is NetworkProfileV1, deprecated, served only to a caller that pinned contract 1, with Deprecation and Sunset headers, until its withdrawal on 2027-03-01.
- 400
- The q parameter is missing, a contract version this deployment does not answer was requested, the query and the Trooth-Contract header asked for different contracts, a section name or limit could not be applied exactly, or an after cursor is malformed, stale or was issued for other sections.
- 429
- Over the per-network budget. Retry-After is the number of seconds until the window that refused resets.
- 503
- Trooth could not read its own record on this load. Retry after the Retry-After interval; nothing is asserted about the company.
curl "https://trooth.co/api/network/profile?q=example.com"{ "contractVersion": 2, "contractNote": "string", "contractOmissions": [ "string" ], "deprecation": "string", "requestedContract": 0, "contractSchema": null, "witnessStatement": { "payload": "string", "signature": "ed25519:BASE64SIGNATURE==", "key_id": "string", "alg": "Ed25519", "canonicalization": "string" }, "witnessEvidenceManifest": [ { "check_id": "string", "source": "string" } ], "signing": { "profileSigned": false, "signedArtifact": "witnessStatement", "subject": "string" }, "assuranceBoundary": { "statement": "This record organizes source-linked information for a security review. It is not an audit, certification, or suitability decision.", "doesNotEstablish": [ "string" ], "methodology": "string" }, "found": true, "slug": "example-co", "name": "string", "domain": "string", "canonicalUrl": "https://trooth.co/network/company/example-co", "tagline": "string", "industries": [ "string" ], "updatedAt": "string", "witnessed": { "standing": "witnessed", "lastWitnessed": "string", "firstWitnessedAt": "string", "coverage": { "checksPassed": 0, "checksRun": 1 }, "continuity": { "recordBegan": "string", "unbrokenSince": "string", "unbrokenReadings": 0, "totalReadings": 0 } }, "methodology": { "cadence": "string", "gapTolerance": "string", "retention": "string", "freshness": [ { "category": "string", "label": "string", "window": "string" } ], "exclusions": [ "string" ], "summary": "string" }, "facts": [ { "key": "string", "category": "string", "label": "string", "value": "string", "origin": "company-declared" } ], "withheldContested": [ "string" ]}GET /api/mobile/directory/search
Search the directory
Search the Trooth Network.
Public Network API, no key · supported
Full-text search over published company listings. Returns the same companies the public directory shows. Prefer cursor pagination: pass the returned next_cursor back as cursor. Offset pagination still works and is kept for clients that already ship with it, but it is deprecated: it counts rows rather than naming a position, so a directory that grows between two requests can serve the same company twice and skip another without erroring. A response to an offset-paged request carries a Deprecation header and a Link header with rel="deprecation"; no withdrawal date is set, so no Sunset header is sent. A cursor that cannot be read returns 400 rather than an empty page. Cached at the edge for 60 seconds with a 300 second stale-while-revalidate window. BEST-EFFORT, MUTABLE ENUMERATION: there is no snapshot. Every page reads the live directory, and the cursor names a position in the sort order, not a point in time; it is not bound to a snapshot, an as-of time or an expiry. A company whose witnessed state changes between two pages can be missed or returned twice, so a walk is not a consistent full export. Drop repeats by slug and walk again to reconcile. The fact pages of /api/network/profile are a different mechanism: their cursor is bound to one record version. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- qstring, in query
- Search terms. Matched against company name and description.
- industrystring, in query
- Restrict to one industry.
- limitinteger, in query
- Results per page. Clamped to 100; anything unreadable falls back to 25.
- cursorstring, in query
- Opaque page marker. Use next_cursor from the previous response. Preferred over offset. It names a position in a live list, not a snapshot: it carries no as-of time and no expiry.
- offsetinteger, in queryDeprecated
- Zero-based offset. Use next_offset from the previous response. Deprecated in favor of cursor; still answered, with a Deprecation header on the response.
Returns
- 200
- A page of results.
- 400
- A cursor that cannot be read.
- 503
- The directory could not be read. Not a statement about any company.
curl "https://trooth.co/api/mobile/directory/search"{ "results": [ { "slug": "string", "name": "string", "standing": "Witnessed" } ], "total": 0, "next_offset": 0, "next_cursor": "string", "error": "string"}GET /api/mobile/directory/featured
List witnessed companies
Companies with a witnessed record.
Public Network API, no key · supported
The Trooth Network's front page: published companies, those with a witnessed record first. Cached at the edge for 120 seconds. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
None.
Returns
- 200
- Published companies.
- 503
- The directory could not be read.
curl "https://trooth.co/api/mobile/directory/featured"{ "results": [ { "slug": "string", "name": "string", "standing": "Witnessed" } ]}GET /api/mobile/directory/vendor/{slug}
Retrieve a company record
One company record.
Public Network API, no key · supported
A published company profile, and its live witnessed state where Trooth has witnessed the company. A slug of the form d--example.com resolves a witnessed company that has not yet published a rich profile. Cached at the edge for 120 seconds. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- slugstring, in pathRequired
- Company slug, or d--<domain>.
Returns
- 200
- The company record.
- 404
- The company is not on the Trooth Network.
- 503
- The record could not be read. Not a statement about the company.
curl "https://trooth.co/api/mobile/directory/vendor/example-co"{ "slug": "string", "name": "string", "standing": "Witnessed", "tagline": "string", "last_scan": "string"}GET /api/network/suggest
Suggest companies
Typeahead over published companies.
Public Network API, no key · supported
Name-to-company suggestions for a search box, over the same published listings the directory shows. Cached at the edge for 60 seconds. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- qstring, in queryRequired
- Partial company name.
Returns
- 200
- Suggestions, possibly none.
curl "https://trooth.co/api/network/suggest?q=example.com"{ "results": [ { "name": "string", "domain": "string", "slug": "string", "witnessed": true } ]}GET /api/public-mesh/{handle}
Retrieve an evidence rollup
A company's public evidence rollup.
Public Network API, no key · supported
The buyer-facing projection of a company's evidence: the witnessed state per area (the pillars key), provider status with the time each connected system was last read. Framework rollups were retired on 2026-09-26 and the frameworks array is always empty. Individual control rows, owners and internal notes are never returned here. Each pillar carries observedAt and sourceRef, null when Trooth holds no per-area read time or reference, or when the area is restricted: null reads as unknown and is never filled from another area, a provider sync or the company's last edit. Responds 404 when the company has no public record, which is a privacy-preserving answer rather than an error. This is the read the embeddable badge performs. Cached at the edge for 300 seconds. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- handlestring, in pathRequired
- Company handle.
Returns
- 200
- The rollup, with framework percentages retired (empty) since 2026-09-26.
- 404
- No public record for this handle.
- 503
- The record could not be read. Not a statement about the company.
curl "https://trooth.co/api/public-mesh/example-co"{ "sample": true, "company": { "name": "string", "handle": "string", "updatedAt": "string" }, "frameworks": [], "pillars": [ { "id": "string", "label": "string", "status": "string", "observedAt": "string", "sourceRef": "string" } ], "providers": [ { "id": "string", "label": "string", "status": "string" } ], "rollup": [ { "pillarId": "string", "label": "string", "status": "witnessed", "counted": true, "evidenceClass": "trooth_observation", "observation": "string", "sourceRef": "string", "observedAt": "string", "validity": { "category": "string", "windowDays": 0, "validUntil": "string" }, "freshness": "current", "scope": { "subject": "string", "scope": "company", "area": "string" }, "unknown": [ "string" ] } ], "badge": { "kind": "witnessed", "line": "string", "detail": "string", "asOf": "string", "counted": 0, "witnessed": 0, "staleAreas": [ "string" ], "undatedAreas": [ "string" ], "restrictedAreas": [ "string" ] }, "controls": [], "freshness30d": 0, "disclaimer": "string", "retired": {}}GET /api/legal/{slug}
Retrieve a legal document
A published legal document.
Public Network API, no key · supported
The full text of one of the twenty published Trooth legal documents, with its title, effective date and body. The slug must be one of the published set; anything else responds 404. Cached at the edge for one hour with a one day stale-while-revalidate window. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- slugstring, in pathRequired
- Document slug, for example privacy, terms, subprocessors.
Returns
- 200
- The document.
- 404
- Unknown document.
curl "https://trooth.co/api/legal/example-co"{ "slug": "string", "title": "string", "effective": "string", "html": "string"}GET /api/version
Retrieve the serving version
The deployment currently serving.
Public Network API, no key · supported
The build stamp of the running deployment. Never cached. Useful for confirming which release answered a request. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
None.
Returns
- 200
- The build stamp.
curl "https://trooth.co/api/version"{ "stamp": "string"}POST /api/network/batch
Check many companies in one call
Check many companies in one call.
Public Network API, no key · supported
A read of up to 25 companies in one request, for an agent that plans a sequence of steps and needs to know, before acting, what each company's record says and whether it changed since planning. Each target is a domain or profile slug, or { q, pin } where pin is the record digest from an earlier read. Each result has status (listed, not_listed, withheld, unavailable), what Trooth's own readings show, the version and SHA-256 digest of the record GET /api/network/profile serves, pin.changed when a pin was sent, and plain day counts. A target that cannot be read is that target's unavailable, with the problem registry's retryable flag and next action; the other targets are still answered. It changes nothing, so it takes no Idempotency-Key and repeating it is always safe. Nothing in it grades, ranks or certifies a company. Errors are always RFC 9457 problem details. Never cached. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
None.
Returns
- 200
- One result per target, in the order sent.
- 400
- The body or a target cannot be used; errors[] points into the body.
- 413
- The body is larger than 16 KiB.
- 429
- Rate limited.
curl -X POST "https://trooth.co/api/network/batch"{ "format": "trooth.network-batch.v1", "checked_at": "string", "summary": { "targets": 0, "listed": 0, "not_listed": 0, "withheld": 0, "unavailable": 0, "changed_since_pin": 0 }, "results": [ { "target": "string", "status": "listed", "problem": { "code": "string", "retryable": true, "next_action": "retry_later", "detail": "string" }, "slug": "string", "name": "string", "domain": "string", "record_url": "string", "profile_url": "string", "witnessed": { "standing": "witnessed", "last_witnessed_at": "string", "first_witnessed_at": "string", "unbroken_since": "string", "checks_passed": 0, "checks_run": 0 }, "record": { "version": 1, "digest": "string", "recorded_at": "string" }, "pin": { "sent": "string", "changed": true }, "facts": { "days_since_last_witnessed": 0, "days_since_profile_updated": 0, "unbroken_readings": 0, "total_readings": 0 } } ], "how_to_read": "string"}GET /api/network/changes
Every record version across the Network
Every record version across the Network.
Public Network API, no key · supported
Every published record version across the Trooth Network after a position, oldest first, at most 100 per page: the company, the version, its SHA-256 digest and the previous one, and where to read it. No record body and no personal fact. Without after or since, the page starts 24 hours back. next is the cursor to send as after, and also the id of the last event, so a client can move between this feed and the event stream without losing its place. Order is by recorded time, ties broken by event id; delivery is at least once across a reconnect (drop repeats by event_id); a company that withdraws its profile stops appearing. A position older than the retention window is 410 beyond_retention. Errors are always RFC 9457 problem details. Never cached. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- afterstring, in query
- next from the previous page, or an event id from the stream.
- sincestring, in query
- Start at this ISO 8601 date or UTC time instead of a cursor.
- limitinteger, in query
- Events per page, 1 to 100 (default 50).
Returns
- 200
- A page of change events.
- 400
- A position or limit that cannot be used.
- 410
- The position is older than the feed keeps (beyond_retention); read the records in full and resume from a fresh position.
- 429
- Rate limited.
- 503
- The feed could not be read, or history is not set up on this database.
curl "https://trooth.co/api/network/changes"{ "format": "trooth.network-changes.v1", "events": [ { "id": "string", "type": "record.version", "event_id": "string", "kind": "published", "subject": { "slug": "string", "domain": "string" }, "version": 1, "recorded_at": "string", "digest": "string", "previous_digest": "string", "corrects_version": 1, "record_url": "string", "history_url": "string" } ], "next": "string", "has_more": true, "stream": "string", "guarantees": { "order": "string", "delivery": "string", "withdrawal": "string", "retention_days": 0, "body": "string" }}GET /api/network/changes/stream
Every record version, as Server-Sent Events
Every record version, as Server-Sent Events.
Public Network API, no key · supported
The change feed as a text/event-stream (WHATWG Server-Sent Events). Each event is event: record.version, with the same JSON an entry of GET /api/network/changes carries and id set to its cursor, so a client that reconnects with Last-Event-ID (every EventSource does) resumes exactly after the last event it received. Without Last-Event-ID or after, the stream starts now. A connection is held for about 50 seconds and closed with a comment; the client reconnects (retry: 3000). A comment is sent every 15 seconds on a quiet stream. Errors before the stream opens are RFC 9457 problem details. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- Last-Event-IDstring, in header
- The id of the last event received; the stream resumes after it.
- afterstring, in query
- The same as Last-Event-ID, for a client that cannot set headers.
Returns
- 200
- An event stream of record.version events.
- 400
- A position that cannot be read.
- 410
- The position is older than the feed keeps.
- 429
- Rate limited.
- 503
- The feed could not be read, or history is not set up.
curl "https://trooth.co/api/network/changes/stream"GET /api/network/search
Discover companies by several criteria
Discover companies by several criteria.
Public Network API, no key · supported
Multi-criteria discovery over the same published companies the directory lists: free text, industry, HQ country, data residency region, deployment model, product name, whether Trooth holds a witnessed reading, and profile changes since a date. A company is matched only by facts its public page shows: residency and deployment are searchable only while the company shows the section that renders them. Filters are exact, case-insensitive matches on what the company published; a company that published nothing for a filtered field is absent from the answer, which is not a no. Order is the directory's: witnessed first, then the most recent reading, then name; never by a figure. Each result carries facts, whole days counted from timestamps on the record (days_since_last_witnessed, days_since_profile_updated), which rate nothing. Cursor pagination only; a cursor is bound to the filters it was issued with. An unknown filter is refused. Errors are always RFC 9457 problem details (codes at /api/problems). Cached at the edge for 60 seconds with a 300 second stale-while-revalidate window. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
- qstring, in query
- Free text, matched against name, tagline, domain, industries and product names.
- industrystring, in query
- An industry, matched as a substring of the company's industries.
- countrystring, in query
- The HQ country, as the company published it; exact, case-insensitive.
- residencystring, in query
- A data residency region, as companies write it (EU, US, UK...); exact, case-insensitive.
- hostingstring, in query
- A deployment model (Cloud, Private cloud / VPC, On-prem, Hybrid); exact, case-insensitive.
- productstring, in query
- A product name, matched as a substring of the visible offering section.
- witnessedstring, in query
- true for companies with a witnessed reading, false for those without.
- updated_sincestring, in query
- Only companies whose profile changed at or after this ISO 8601 date or UTC time.
- limitinteger, in query
- Results per page, 1 to 100 (default 25).
- cursorstring, in query
- next_cursor from the previous page, sent with the same filters.
Returns
- 200
- A page of matching companies.
- 400
- An unknown filter, a value that cannot be used, or a cursor issued for different filters.
- 429
- Rate limited.
- 503
- The directory could not be read. Not an empty result.
curl "https://trooth.co/api/network/search"{ "format": "trooth.network-search.v1", "results": [ { "slug": "string", "domain": "string", "name": "string", "record_url": "string", "profile_url": "string", "witnessed": true, "last_witnessed_at": "string", "profile_updated_at": "string", "industries": [ "string" ], "products": [ "string" ], "country": "string", "residency": [ "string" ], "hosting": [ "string" ], "facts": { "days_since_last_witnessed": 0, "days_since_profile_updated": 0 } } ], "total": 0, "next_cursor": "string", "partial": { "shown": 0, "total": 0 }, "order": "string", "how_to_read": "string", "consistency": { "mode": "best-effort", "completeEnumeration": false, "note": "string", "cursorScope": "string" }}GET /api/problems
Every error code and whether retrying can help
Every error code and whether retrying can help.
Public Network API, no key · supported
The registry every RFC 9457 problem document from trooth.co draws on: each code with its status, fixed title, retryable flag, next action, and terminal (true when repeating the same request cannot change the answer, exactly not retryable). A code keeps its meaning once published. Cached for one hour. Readable cross-origin: the response carries Access-Control-Allow-Origin: * and never Access-Control-Allow-Credentials, so a browser on any site may read it without cookies.
Parameters
None.
Returns
- 200
- The registry.
curl "https://trooth.co/api/problems"{ "format": "trooth.problem-catalog.v1", "media_type": "application/problem+json", "next_actions": [ "retry_later" ], "problems": [ { "code": "string", "type": "string", "status": 100, "title": "string", "retryable": true, "next_action": "retry_later", "terminal": true } ], "how_to_read": "string"}For AI agents
Retrieval agents are welcome (robots.txt); model-training crawlers are blocked, since published companies did not consent to wholesale ingestion.
Cite each fact's source, and its date where it has one. When Trooth holds no record, say so.
The Model Context Protocol (MCP) server is live at https://api.trooth.co/public/mcp (JSON-RPC 2.0, public, no key). It leads with 2026-07-28, reports trooth-mcp 1.8.0 and still answers 2025-11-25, 2025-06-18, 2025-03-26 or 2024-11-05.
- 10 tools without an account (
trooth_public_trust_profile,trooth_outside_in_read,trooth_verify,trooth_ask,trooth_ai_data_use_disclosures,trooth_search_companies,trooth_resolve_company,trooth_get_fact,trooth_list_companiesandtrooth_company_changes), and 1 more tool that reads only the caller's own workspace with an OAuth access token. Each is read only and returnsstructuredContent(status, provenance, subject, summary); the table below states each one's access, what it returns, its argument limits and its cost. - 7 resources:
trooth://buyer-questions,trooth://methodology,trooth://provenance-glossary,trooth://provenance-labels,trooth://schema,trooth://verify-a-vendor,trooth://what-a-call-sends. - 4 prompts:
vendor_trust_check,verify_trust_token,before_you_trust,vendor_due_diligence.
| Tool | Access | Returns | Arguments | Units per call |
|---|---|---|---|---|
trooth_public_trust_profileone company's complete Trust Profile on the Trooth Network, every section with its state and who said it, after a short summary that links the full record | Public, no account | Public record data |
| 1 |
trooth_outside_in_reada live, neutral read of a domain's public surface right now: HTTPS reachability, five common response headers and security.txt | Public, no account | Externally fetched data, read from the named domain at call time |
| 4 |
trooth_verifyre-check the two signatures on a Trust Ledger Token | Public, no account | A signature check of the token you send |
| 1 |
trooth_aska question about Trooth itself, answered from its fixed knowledge base | Public, no account | Trooth's own public documents |
| 1 |
trooth_ai_data_use_disclosureswhat one company has declared about AI and customer data, each row labeled as the company's own declaration | Public, no account | Public record data |
| 1 |
trooth_search_companiessearch the companies published on the Trooth Network by name, domain, tagline or category | Public, no account | Public record data |
| 1 |
trooth_resolve_companyfind a company's published record from a domain, web address, Trooth slug or name, with why it matched, or every candidate when several match and none is chosen | Public, no account | Public record data |
| 1 |
trooth_get_factone answer from one company's record, by buyer question ID or fact ID, with its provenance word, date and anchor address, or which of no, not_published or not_read applies | Public, no account | Public record data |
| 1 |
trooth_list_companieslist the companies published on the Trooth Network by industry, data region, witnessed or not, and claimed or not, in alphabetical order of record identifier | Public, no account | Public record data |
| 1 |
trooth_company_changeshow one company's published record changed, version by version, oldest first, with each fact's edits when the record carries them | Public, no account | Public record data |
| 1 |
trooth_my_company_recordyour own workspace's company record, after sign-in | OAuth access token; reads only your own workspace | Caller-private data | None | 1 |
trooth_profile_getyour own workspace's Trust Profile draft as saved now, its revision, whether it is published and its public address | Your sign-in, or the workspace's agent key for an agent that runs on its own; reads only your own Trust Profile draft | Caller-private data | None | 1 |
trooth_profile_checklistthe steps left before your workspace's Trust Profile can be published, and how to do each | Your sign-in, or the workspace's agent key for an agent that runs on its own; reads only your own Trust Profile draft | Caller-private data | None | 1 |
trooth_profile_updatechange fields of your workspace's Trust Profile draft, each value with the https source it was read from, with your sign-in; the public page changes only when the draft is published | Your sign-in, within your role, or the workspace's agent key for an agent that runs on its own; writes your own Trust Profile | Caller-private data |
| 2 |
trooth_profile_publishpublish your workspace's saved Trust Profile draft so anyone can read it, with your sign-in; a first publish needs the company's domain proved, which the two domain tools do | Your sign-in, within your role, or the workspace's agent key for an agent that runs on its own; writes your own Trust Profile | Caller-private data | None | 2 |
trooth_profile_domain_startstart proving your company's domain: the DNS TXT record to add with your DNS host's own API | Your sign-in, within your role, or the workspace's agent key for an agent that runs on its own; writes your own Trust Profile | Caller-private data |
| 2 |
trooth_profile_domain_checkcheck the domain proof in DNS now; once it is there, the domain is proved | Your sign-in, within your role, or the workspace's agent key for an agent that runs on its own; writes your own Trust Profile | Caller-private data |
| 2 |
Setup: Agents and MCP. Changes: changelog.
https://api.trooth.co/public/mcp{ "mcpServers": { "trooth": { "type": "http", "url": "https://api.trooth.co/public/mcp" } }}Webhooks and SDKs
Companies set webhooks in their workspace. Agents and buyers have one public route, POST https://api.trooth.co/public/trust/:slug/agent-subscribe:
- Send a
webhookUrland get back a signing secret. - It follows published profiles only.
- It sends
trust.posture.changed, signed withx-trooth-signature; workspace endpoints have their own events.
Details are on Agents and MCP.
There is no full client SDK and no scoped Trooth package; any @trooth/ name is not ours. Libraries that check Trooth's signed objects exist for JavaScript (in the trooth package), Python and Go, all in the trooth-cli repository. For the API itself, use plain HTTP and JSON, or generate a typed client from /openapi.json.
One package is published to a registry: trooth on npm (source: github.com/troothllc/trooth-cli), version 0.22.1. Two of its commands:
trooth check <domain>reads a company's record on the Trooth Network.trooth lint [path]reads what your repository declares and makes no request.
It needs no key and no account. Its other commands read a company's Trust Profile, check Trooth's signed witness statement and its public log, read what a company filed with regulators and registries, check a domain's signed declaration, decide an agent's action under your own policy, and check delegation receipts and action evidence references; npx trooth --help lists every one. trooth scan was retired and exits with an explanation.
POST https://api.trooth.co/public/trust/example.com/agent-subscribeContent-Type: application/json{ "webhookUrl": "https://your-service.example/hooks/trooth" }npx trooth check example.comnpx trooth lint .Before you go to production
Six checks, each a failure an integration has already had somewhere.
- Handle every class of answerA 200 with found: false, 400, 404, 429 and 5xx each need a branch. Never record a 503 as a finding.
- Bound retries and pagesRetry 429 and 5xx only, with backoff and a cap, then surface the failure. Cap cursor pages; never resend a cursor.
- Stay inside the limitsThe record lookup allows 600 requests a minute and 20,000 an hour per client network. Other reads are edge-cached.
- Pin the contract and watch for deprecationSend the contract header, read the deprecation field, and treat Deprecation or Sunset headers as dated tasks.
- Carry provenance and dates to your usersShow origin beside each value, age facts by the record's last reading, and never render unknown as no.
- Know what is promisedThe service level agreement and API terms state Trooth's commitments and remedies. Read both first.
The terms are the service level agreement and the API terms.
| What | Where |
|---|---|
| Live availability | /status |
| Incident history, as a feed | /status/history.rss |
| API changes, as a feed | /changelog/rss.xml?area=api |
| Security changes, as a feed | /changelog/rss.xml?type=security |
| Versioning policy | /api-versioning |
| A person | developers@trooth.co |
Changes and status
Breaking changes are announced first in the changelog; policy: API versioning. Availability: status; GET /api/version names the answering deployment.
Questions go to developers@trooth.co.
curl "https://trooth.co/api/version"# { "stamp": "..." }Something wrong or missing? Write to developers@trooth.co; corrections appear in the changelog.
Help test this reference.Fifteen minutes, five tasks, no account.
The same record, readable by both.
Security reviewers and AI procurement agents read one record, with sources, dates and signatures.