Server adapters and raw HTTP
You can call the server API with the dependency-free adapter files this Telltale server serves, or directly over HTTP from any language. No npm package is published.
Download the adapters
Each file is plain JavaScript with no dependencies. The table is generated from the files this Telltale server serves right now:
| File | URL | Runtime | Integrity (SHA-256) |
|---|---|---|---|
| server.js | https://app.trytelltale.dev/adapters/v1/server.js | Node 18+ (node:crypto, node:timers/promises) | sha256-40PkaM2umvQAJFO8K1ldGkc21DBHVKx8NOWTujk/6PM= |
| identifiers.js | https://app.trytelltale.dev/adapters/v1/identifiers.js | Any | sha256-7WhD86As429qJgiaPyDii+9CSY0T1jjVBecEd4cFZlk= |
| browser.js | https://app.trytelltale.dev/adapters/v1/browser.js | Browser (ES module) | sha256-mzfT5t6/cM7/MEh16BWUywk8DX8yYoJvFxr9uHiTsb4= |
| better-auth.js | https://app.trytelltale.dev/adapters/v1/better-auth.js | Browser (ES module) | sha256-3JGANqoHsA+7j1wIKrbbl4SiFj/+K23sePAYs2UoCFY= |
| better-auth-server.js | https://app.trytelltale.dev/adapters/v1/better-auth-server.js | Server (Fetch API Request/Response) | sha256-vAIWVsGB0ubnxTmnl9xsB7eBi5yto3GTtu4k0Ib58U0= |
| cmp.js | https://app.trytelltale.dev/adapters/v1/cmp.js | Browser (ES module) | sha256-VQPMF3uzbxqv5e3nUPXVVlbXYy/zZE6/oaPkSgDsWwc= |
| identity-digest.js | https://app.trytelltale.dev/adapters/v1/identity-digest.js | Browser or server (Web Crypto) | sha256-pYyUJCGyvvv6msGANwAoSzu7AHVIGLpac9Obi2oxZZs= |
| gpp.js | https://app.trytelltale.dev/adapters/v1/gpp.js | Node 18+ or browser (ES module) | sha256-/PJkVslA3oudeIJ8EIaM9NBcB8sp7IS5FT9NC+pE5VY= |
| api.js | https://app.trytelltale.dev/adapters/v1/api.js | Node 18+ or any runtime with fetch and Web Crypto | sha256-qfQmeMfEz7yld9zHdC+mvvswgMrNhtAE+PW0svFsnCk= |
| mcp.js | https://app.trytelltale.dev/adapters/v1/mcp.js | Node 18+ or any runtime with fetch and Web Crypto | sha256-PuQO7br8r6hqHJww2efp73EEe5eN8ZHHiVVGk+fS2Uo= |
| server.d.ts | https://app.trytelltale.dev/adapters/v1/server.d.ts | TypeScript declarations | sha256-vC+BUCLRiBrwCfafKMvCqmn35qNMSg0h5sydPvlba2Q= |
| identifiers.d.ts | https://app.trytelltale.dev/adapters/v1/identifiers.d.ts | TypeScript declarations | sha256-v00ATdPyRPL5jJyUF+6MYDaIfTrdQx41FukPdD/sn08= |
| browser.d.ts | https://app.trytelltale.dev/adapters/v1/browser.d.ts | TypeScript declarations | sha256-zYsTg7gScpiaTbP4uC6j5taEVvTi5tDzEJG9JYWbNZo= |
| better-auth.d.ts | https://app.trytelltale.dev/adapters/v1/better-auth.d.ts | TypeScript declarations | sha256-V/m6bieX2MAjWrVrmfzBZxEZIwFJSiXG96P6BDUOJEs= |
| better-auth-server.d.ts | https://app.trytelltale.dev/adapters/v1/better-auth-server.d.ts | TypeScript declarations | sha256-EMWd/YtfcLfPbTjxNbhXU5jat5kNxGDRCJT+azLLzZk= |
| cmp.d.ts | https://app.trytelltale.dev/adapters/v1/cmp.d.ts | TypeScript declarations | sha256-MBI7C8Uc+v9A7sWnUAUY0HGWUAdxGAyVJDVP2D0gS34= |
| sdk.d.ts | https://app.trytelltale.dev/adapters/v1/sdk.d.ts | TypeScript declarations | sha256-U/1l+tDz/wcbgZGx9mH21LKuKs0DR/Qz4b6cceJvJd4= |
| mcp.d.ts | https://app.trytelltale.dev/adapters/v1/mcp.d.ts | TypeScript declarations | sha256-4fxKCtTo0FvfFMDlCB6GQZJQ3tOfSiB//pWlFsLhj8A= |
| api.d.ts | https://app.trytelltale.dev/adapters/v1/api.d.ts | TypeScript declarations | sha256-ztd27zvuP1tH0RcOyE1zF5TBPzOl9hCppHFyxojZQ88= |
server.js imports ./identifiers.js, and better-auth.js imports ./browser.js and ./identity-digest.js, so download a file's companions into the same folder. Every .js file is also served at the unversioned alias /adapters/NAME.js. That alias is deprecated and will be removed on April 30, 2027: its responses carry Sunset, Deprecation and a Link to the /adapters/v1/ path. Use the /adapters/v1/ path in code you commit.
v1 is the adapter API generation, not a frozen byte stream. Record the integrity you reviewed and check it whenever you download again:
mkdir -p lib/telltale && cd lib/telltale
curl -fsSO https://app.trytelltale.dev/adapters/v1/server.js
curl -fsSO https://app.trytelltale.dev/adapters/v1/identifiers.js
printf '{ "type": "module" }\n' > package.json # the adapters are ES modules, even in a CommonJS project
# Compare with the Integrity column above (sha256-BASE64):
echo "sha256-$(openssl dgst -sha256 -binary server.js | openssl base64 -A)"The adapters are ES modules. The package.json line marks that folder as ES modules, so they load in a CommonJS project too (what npm init writes): import them from an ES module (a .mjs file, or any file in a "type": "module" project), or require() them on Node 22.12 or later.
Responses also carry a Repr-Digest header (sha-256=:BASE64:) with the same value. Vendor the files into your repository rather than importing them from the Telltale server at runtime.
TypeScript types
Each adapter has a declaration file beside it (the .d.ts rows in the table): server.d.ts, identifiers.d.ts, browser.d.ts, better-auth.d.ts, better-auth-server.d.ts and cmp.d.ts. sdk.d.ts types the hosted SDK global, window.Telltale, including client.status(), getSessionToken({timeoutMs}) and the network_unavailable error code. Download the .d.ts next to its .js file and TypeScript picks it up:
curl -fsSO https://app.trytelltale.dev/adapters/v1/server.d.ts
curl -fsSO https://app.trytelltale.dev/adapters/v1/identifiers.d.ts
curl -fsSO https://app.trytelltale.dev/adapters/v1/sdk.d.ts # browser code: import type { TelltaleClient } from './sdk.js'MCP and API middleware
api.js is Telltale for APIs, the middleware for agents calling your own API, and mcp.js is Telltale for MCP servers. gpp.js decodes an IAB GPP consent string on your server (Consent bridge). Download them like the others and check their integrity the same way.
Use the server adapter
import { createTelltaleServer } from './lib/telltale/server.js';
const telltale = createTelltaleServer({
endpoint: 'https://app.trytelltale.dev',
serverKey: process.env.TELLTALE_SERVER_KEY
});
const evidence = await telltale.verify({ sessionToken, actionId: 'export_report' });
// evidence.verdict is human, agent or unknown; evidence.decision is allow, review or deny.The adapter validates every response, applies a five-second deadline, preserves your caller's cancellation and retries verification only after a valid 503 response, or once after a 408 or 429 whose Retry-After still fits inside the deadline (at most three attempts in that deadline). requireAllowed() rejects review and deny. identify() and recordUsage() never retry automatically. verifyWebhook() checks Standard Webhooks deliveries (Webhooks guide). Your application still owns authentication, account authorization and the protected action.
Raw HTTP
Every operation takes the Site server key as a bearer token and JSON bodies. The API reference pages and /openapi.json are generated from the running contract; if anything here differs, they win. The same verification call with curl, or with fetch for runtimes without the adapter:
curl -sS -X POST https://app.trytelltale.dev/api/v1/verify \
-H "Authorization: Bearer $TELLTALE_SERVER_KEY" \
-H 'Content-Type: application/json' \
--max-time 5 \
--data '{"sessionToken":"TOKEN_FROM_THE_BROWSER","actionId":"export_report"}'async function verify(sessionToken, actionId, signal) {
const deadline = AbortSignal.timeout(5000);
const response = await fetch('https://app.trytelltale.dev/api/v1/verify', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.TELLTALE_SERVER_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionToken, actionId }),
// AbortSignal.any() throws on undefined, so combine only when the caller passed a signal.
signal: signal ? AbortSignal.any([signal, deadline]) : deadline
});
const body = await response.json();
if (!response.ok) throw Object.assign(new Error(body.code || 'verification_failed'), { status: response.status, code: body.code });
if (!['human', 'agent', 'unknown'].includes(body.verdict) || !['allow', 'review', 'deny'].includes(body.decision)) throw new Error('invalid_verification_response');
return body; // { verdict, score, decision, stale, policy, reasons, ... }
}Rules that apply whatever client you use:
- Verify immediately before the protected action, on your server, after your own authentication and input validation.
- A failure is not a verdict. Check the code field: unauthorized means your server key is wrong or missing the Bearer scheme, or the session token is not one this Telltale server issued (malformed, edited or from another Telltale server); session_reinit_required means the browser needs a fresh SDK session and token, so do not resend the old token. Errors and retries lists every code.
- Retry only verification: after a 503 service_unavailable body (small random backoff), or once after a 408 or 429 when its Retry-After fits inside the five-second deadline; at most three attempts within five seconds. Never repeat the protected action itself.
- Unknown is never human. Scores (1, 99, 50) are categorical, not probabilities.
Server-verified identity (POST /api/v1/identify) and metering (POST /api/v1/usage) use the same headers; see their API reference pages and the Usage metering guide.