Skip to main content

Authentication and keys

Last updated: Oct 11, 2026View as Markdown

The Trooth application programming interface (API) is public and versioned. Its reads need no key or account; the public calls that change state, such as subscribing to a company's changes, are marked with their side effects in the public interface catalog.

Quickstart

Keyless: the Trust Profile read, the API reference operations and the Model Context Protocol (MCP) server. No credentialed API exists for integrators; workspace endpoints are undocumented.

A workspace API key has one use: sending evidence events into your own workspace (Connect, API and webhook).

  • Keys are workspace-scoped, shown once and revocable.
  • No other endpoint is documented for keys.
  • Webhooks fire on the events listed in the developer docs; delivery is attempted, not guaranteed.

Example request

# Reads a company's published Trust Profile by its domain. No key, no account.curl -s "https://trooth.co/api/network/profile?q=your-co.com&contract=2"

Authentication and rate limits

No authentication. Limits, where they apply, are per IP address, not per tenant:

  • This site: per-minute and per-hour windows (lib/rate-limit.ts).
  • Trooth API: no rate limit on the /public/trust read.
  • MCP server: 120 tool calls a minute per IP, per instance, so approximate; listing and handshake uncounted. Over it: 429, Retry-After: 60.
  • No plans: Trooth is free, so allowances are equal.
  • Webhooks are HMAC-signed.

Authorization model

The Trooth application programming interface (API) at api.trooth.co accepts four kinds of customer credential. Sessions, bound API keys and scoped tokens are sender-constrained with Demonstrating Proof of Possession (DPoP, RFC 9449): each is accepted only together with a fresh proof signed by the private key it is bound to. A copy of the credential alone, taken from a log, a proxy, a crash report or a leaked environment, is refused.

The credentials

  • Session token. Minted when a person signs in and held in an httpOnly, Secure, SameSite=Lax cookie on trooth.co, where page scripts cannot read it. api.trooth.co accepts it only from trooth.co's own server, with a proof signed by trooth.co's server key. Sent from anywhere else, as a bearer or with any other key's proof, it is refused. It lasts 30 days at most and is revoked from Account security.

  • Workspace API key (tk_live_). Created by a member of the workspace with a confirmed address, and stored only as a SHA-256 hash. A key created with dpop_jkt, the RFC 7638 thumbprint of your public key, is bound to that key: only its proofs are accepted, everywhere, and a plain bearer is refused. A key created without one is an ingest key: it reaches one endpoint, POST /v1/evidence/webhook, which appends an evidence event your company asserts about itself, so a tool that cannot sign a proof can still report. An ingest key reads nothing and changes nothing else. Revocation takes effect on the next request.

  • Scoped token (st_). Minted by a full credential for one named operation, for 15 to 60 minutes, to hand to an agent, and bound at mint to the agent's key: dpop_jkt is required.

  • OAuth access token (Model Context Protocol and Agent2Agent). Issued by Trooth's authorization server for exactly one resource, https://api.trooth.co/public/mcp or https://api.trooth.co/a2a/v1 (RFC 8707 audience, RFC 9728 metadata). It reaches the read-only Model Context Protocol tools and the Agent2Agent tasks its own subject created. When the authorization server binds the token to a key (the cnf.jkt claim), that key's proof is required and a plain bearer is refused. A token issued unbound is a bearer token, limited to its audience and lifetime and to reading the signed-in workspace's own record.

Trooth's own operations (the operator key, scheduled jobs, webhook signatures) use separate headers or signatures, are never customer credentials, and resolve no workspace.

The proof

Send the token in the Authorization header with the DPoP scheme, and one proof in the DPoP header, new for every request. The proof is a JSON Web Signature (JWS) with:

  • header typ dpop+jwt, alg ES256 or EdDSA, and your public key in jwk, with no private member;
  • claims htm (the request method), htu (the request URL without query or fragment), iat (now; accepted from five minutes before to one minute after the API's clock), a unique jti, and ath, the base64url SHA-256 of the access token.

A proof is accepted once: its jti is remembered for ten minutes, and a second use is refused. A refusal answers 401 with WWW-Authenticate: DPoP and an error of dpop_required, invalid_dpop_proof or invalid_token.

// npm i jose   (any JOSE library that signs ES256 or EdDSA works)import { generateKeyPair, exportJWK, calculateJwkThumbprint, SignJWT } from "jose";import { createHash, randomUUID } from "node:crypto";// Make the key once and keep the private half where your client runs.const { publicKey, privateKey } = await generateKeyPair("ES256");const jwk = await exportJWK(publicKey);const dpopJkt = await calculateJwkThumbprint(jwk); // send as dpop_jkt when you create the API keyasync function call(method, url, token, body) {  const u = new URL(url);  const proof = await new SignJWT({    htm: method,    htu: u.origin + u.pathname,    jti: randomUUID(),    ath: createHash("sha256").update(token).digest("base64url"),  })    .setProtectedHeader({ typ: "dpop+jwt", alg: "ES256", jwk })    .setIssuedAt()    .sign(privateKey);  return fetch(url, {    method,    headers: {      authorization: `DPoP ${token}`,      dpop: proof,      "content-type": "application/json",      ...(method === "GET" ? {} : { "idempotency-key": randomUUID() }),    },    body: body ? JSON.stringify(body) : undefined,  });}const apiKey = process.argv[2]; // the bound key, passed in from your secret store, never in sourceconst res = await call("POST", "https://api.trooth.co/v1/evidence/webhook", apiKey, {  evidence_type: "runtime_log",  summary: "Nightly guardrail policy enforcement",  status: "observed",  source: "your-tool",});if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);

Tenant isolation

The workspace a request acts on comes from its credential and nothing else: never from a header, a path, a query parameter or a body. Every database read and write is bound to that workspace. Before each release the isolation suite runs against the Worker that serves the API, with two workspaces and a client holding copies of one workspace's tokens; a release with a failing assertion does not deploy.

Idempotency

Every POST, PUT, PATCH and DELETE accepts an Idempotency-Key header of 8 to 255 printable characters. The first request with a key runs and its answer is kept for 24 hours; the same key with the same request returns that answer with Idempotency-Replayed: true and runs nothing; the same key with a different request is refused with 422; a key whose first request is still in progress answers 409. A 5xx, 401, 403, 408, 409, 425 or 429 answer is not kept, so a retry runs again. Keys belong to the credential that sent them, and a kept answer is encrypted under a key derived from that credential and the request.

Audit log and export

Each workspace's audit log holds its sign-ins, password, two-factor and address changes, API keys, every write made with one of its credentials (method, path, status, which credential, whether it was a replay) and every refused attempt to use one of its credentials without the proof it is bound to. No token and no request body is written. Download the whole log from Audit log in the workspace as CSV or JSON Lines, oldest first, 5,000 rows a file.

What this does not cover

  • A session cookie stolen out of a browser still works in a browser at trooth.co until it expires or is revoked. Binding the browser itself to a key is not yet in place.
  • An OAuth access token the authorization server issues unbound remains a bearer token for its lifetime.
  • An ingest API key is a bearer credential for its one endpoint: a stolen copy can append self-asserted evidence events to the workspace until the key is revoked, and each one is in the audit log. A key created with dpop_jkt has no such use.

MCP server for AI agents

AI agents can query Trooth over the Model Context Protocol. The public tools are read-only and keyless: Trust Profiles, AI and data-use declarations, outside-in reads, token checks, the knowledge base and provenance labels. One more tool reads your own workspace after sign-in. It needs an OAuth access token for the MCP server from the authorization server its protected-resource metadata names, with no scope, and it reads only the workspace linked to you when you signed in to trooth.co with Continue with enterprise SSO. The A2A agent keeps a task for a token holder for 24 hours. The agents guide describes the sign-in flow.

  • Connect: point an MCP client that connects to remote servers at the endpoint below (streamable HTTP, one JSON response per POST, protocol 2026-07-28, server trooth-mcp 1.4.1).
  • Older revisions: 2025-06-18, 2025-03-26 or 2024-11-05 still work unchanged.
  • Clients: the block below is a project .mcp.json entry (Claude Code, VS Code). Claude.ai and Desktop: Customize, Connectors, Add custom connector; ChatGPT lists Trooth Network as an app.
  • Limits and removal: per client, on the agents page.
{  "mcpServers": {    "trooth": {      "type": "http",      "url": "https://api.trooth.co/public/mcp"    }  }}

Tools take one required string and return structuredContent (status, provenance, subject, summary) with the text:

  • trooth_public_trust_profile (company): one 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
  • trooth_outside_in_read (domain): a live, neutral read of a domain's public surface right now: HTTPS reachability, five common response headers and security.txt
  • trooth_verify (token): re-check the two signatures on a Trust Ledger Token
  • trooth_ask (question): a question about Trooth itself, answered from its fixed knowledge base
  • trooth_ai_data_use_disclosures (company): what one company has declared about AI and customer data, each row labeled as the company's own declaration
  • trooth_search_companies (query): search the companies published on the Trooth Network by name, domain, tagline or category
  • trooth_compare_companies (companies): two to four companies' Trust Profiles side by side, section by section, each cell the company's own record

Also: 4 markdown resources (trooth://methodology, trooth://provenance-labels, trooth://verify-a-vendor, trooth://what-a-call-sends) and 3 prompts (vendor_trust_check, verify_trust_token, before_you_trust). Rate limited per IP; no tenant data; full contract on Agents and MCP.

Every interface, in one table

A summary of the supported interfaces. The controlling list, with each one's access, side effects, limits and availability on this deployment, is /interfaces.json. Anything not listed there is internal, unsupported and may change without notice.

InterfaceHostAccessEffectSupportDocumented at
Canonical profile, GET /api/network/profiletrooth.coPublic, no credentialReadSupported, contract 2/openapi.json
The other eight public reads in /openapi.jsontrooth.coPublic, no credentialReadSupported/docs/api
GET /public/trust/:slug and the directory feedapi.trooth.coPublic, no credentialReadSupported; older than the canonical profile, which is preferredTrust Profile API
MCP server, seven public toolsapi.trooth.co/public/mcpPublic, no credentialReadSupportedAgents and MCP
Profile subscriptions (agent-subscribe)api.trooth.coPublic; the destination must answer a challengeCreates a subscription and sends deliveriesSupportedAgents and MCP
Command-line interface (CLI), trooth check, verify and lintapi.trooth.co (check only)Public, no credentialReadSupportedCLI
Workspace evidence eventsapi.trooth.coWorkspace API keyWrites to your own workspaceSupported, one useConnect, API and webhook, in the workspace
Webhooks and alert destinationsOutbound from TroothConfigured in a signed-in workspaceDeliveries to your endpointSupportedWebhooks
WebMCP profile toolsIn the signed-in editor, in your browserYour own sessionDrafts, saves and publishes declared fields, as the workspace allowsExperimentalBuild with AI
Session routes behind the website and workspacestrooth.coSigned-in sessionVariesInternal, not supportedNot documented
MCP tool trooth_my_company_recordapi.trooth.co/public/mcpOAuth access token for this server, no scopeReads your own workspaceDocumented, not yet versionedAgents and MCP
A2A agent: /a2a/v1 and the Agent Cardapi.trooth.coPublic, no credential; a task is kept only for a caller with an OAuth access token, with no scopeReadSupportedAgents and MCP
Record versions: GET /api/network/profile/historytrooth.coPublic, no credentialReadDocumented, not yet versioned/interfaces.json

Pagination and completeness

Directory search: pass next_cursor back as cursor until null. A cursor is a position in a live list, not a snapshot, so a walk is best-effort discovery and not a consistent full export.

Walks are not atomic: mid-walk changes may appear later or be passed over. No tombstones; removed companies are absent.

To hold a complete copy:

  1. Walk to the end twice and compare slug sets.
  2. In both walks: current. Only in the second: new.
  3. Only in the first: removed or unpublished; GET /api/network/profile?q=<slug> confirms with found: false.
  4. Repeat until two walks agree.

An unreadable cursor gets 400, never an empty page.

The API is free.

Every endpoint, joining, listing and witnessing are free. Nothing is for sale, including any position on any Trooth page.

Read the full API reference.

Use of the Trooth API is subject to the Terms of Service.

REST API: reference

Quick start
Read one published profile with the request under Example request. See this section.
Prerequisites
An HTTP client. No key and no account for the public endpoints.
Examples
The curl samples are copyable, and tests check them against the OpenAPI contract. See this section.
Access and data boundaries
Public reads return only published or publicly observed data, with no key. Subscribing an endpoint or an address to a company's changes is a public call that stores state and makes Trooth send requests later. Private workspace reads over MCP and A2A need an OAuth access token with no scope, and browser publication happens only in the signed-in editor. Workspace endpoints are not documented for integration. See this section.
Uncertainty in answers
Each fact carries its origin. A shared name lists every slug instead of guessing. See this section.
Versions
Pass contract=2 for the current public contract. An unsupported contract value is refused with unsupported_contract_version and the version the server speaks.
Errors
No published profile: found: false. Unreadable record: 503 with a reason and Retry-After, never an empty profile. Unsupported contract: 400. See this section.
Checking the result
The profile's witness statement is signed; check it against /verify/keys before relying on it.
Revocation and removal
Not applicable. Public reads take no credential. Revoke a workspace API key under Connect, API and webhook.
How the examples are held
Executed by tests against public/openapi.json.

Troubleshooting

  • A company you expected answers found: false. Search by website domain, not name; a shared name returns slugs, not a profile.
  • The response is 503 with record_unavailable or witnessed_record_unavailable. Trooth could not read its own record. Retry after the stated seconds; it does not mean no profile.
  • Fields you used are missing. Check the contract version you asked for. contract=2 is current; older shapes are documented in the reference table.

Markup-free twin (llms.txt): reference

Quick start
Fetch /network/company/<slug>/llms.txt for any published company.
Prerequisites
An HTTP client that accepts text/markdown. No key.
Examples
Built by the editor's machine-preview function; two renders of one record are byte-identical.
Access and data boundaries
Only the published record: never the draft or documents under NDA.
Uncertainty in answers
An unsupplied fact reads as not supplied; an unreadable witnessed record says so under Record on the Trooth Network.
Versions
The file states when the published record last changed.
Errors
An unpublished or unknown company answers 404.
Checking the result
The twin points to the signed JSON record; verify that, not the text.
Revocation and removal
Not applicable. No credential. Unpublishing the profile removes the twin.
How the examples are held
Executed by tests against lib/network/llms-twin.ts buildCompanyLlmsText, the function the public route serves.

Troubleshooting

  • The twin is older than the page you edited. The twin follows the published record, never the draft. Publish your edits.
View as agent