{"openapi":"3.1.1","info":{"title":"Telltale Server API","version":"1.0.0","description":"Server-to-server verification, identity, completed-work metering and agent usage reports. Keep bearer keys off browsers. This public contract is separate from each Site’s discovered customer API export. Browser SDK and dashboard administration are not covered."},"servers":[{"url":"https://app.trytelltale.dev"}],"security":[{"siteServerKey":[]}],"components":{"securitySchemes":{"siteServerKey":{"type":"http","scheme":"bearer","description":"Site server key in Authorization: Bearer <key> (the scheme is case-insensitive and required; a bare key is 401). Shown once on creation or rotation. Publishable keys cannot authenticate."}},"schemas":{"CompatError":{"type":"object","properties":{"error":{"type":"string","enum":["bad_request","unauthorized","forbidden","not_found","session_reinit_required","rate_limited","internal_error","config_unavailable","session_unavailable","discovery_not_ready","discovery_unavailable","session_not_found"]}},"required":["error"],"additionalProperties":false,"description":"Any compatibility API (/v1) failure body, exactly {\"error\":\"<code>\"}. Each operation response names its per-code schema (BadRequestError, UnauthorizedError, ...). Failures are not verdicts."},"Error":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["bad_request","unauthorized","session_reinit_required","forbidden","not_found","conflict","identity_required","payload_too_large","rate_limited","internal_error","service_unavailable","request_failed","approval_invalid","approval_used","approval_expired","approval_mismatch","step_up_not_held","step_up_mismatch","step_up_settled","step_up_expired","step_up_superseded","agent_tokens_disabled","tool_not_approved","tool_not_read_only"]},"hint":{"type":"string","enum":["server_key","session_token"],"description":"On 401 unauthorized: which credential was refused, never its value."},"message":{"type":"string","maxLength":300,"description":"On bad_request, not_found, conflict, identity_required, payload_too_large and the agent-token refusals (agent_tokens_disabled, tool_not_approved, tool_not_read_only): a sentence saying what to fix. For people and logs; branch on code, never on message."},"field":{"type":"string","description":"On 400: the request field the message refers to, such as limit, cursor, from, eventId or units."}},"required":["error","code"],"description":"error and code contain the same stable code. Failures are not verdicts. 5xx bodies carry only error and code."},"Attribution":{"type":"object","properties":{"strength":{"type":"string","enum":["signed","weak-marker","unattributed-automation"],"description":"signed: verified Web Bot Auth signature from a reviewed operator directory. weak-marker: a renamable page marker beside an agent verdict. unattributed-automation: agent evidence without a naming signal. Opt-in (request header Observatory-Attribution: declared, sent by the current server adapter): the value declared may also appear, meaning the client named itself in its user agent (self-declared, spoofable; attribution only, never verdict evidence, never a usage category), with kind self-declared and evidence type declared-user-agent. Without the header these values never appear and attribution is computed as if no declaration was sent."},"controller":{"type":["string","null"],"description":"Named only together with strength. Null for unattributed automation."},"operator":{"type":["string","null"],"description":"Web Bot Auth operator origin; present only for signed."},"operatorType":{"type":"string","enum":["direct","intermediary"],"description":"Signed only, when the reviewed registry says. direct: the operator runs the agent. intermediary: a cloud browser platform that many customers' agents run on; the name is the platform, not the customer agent."},"cloudRun":{"type":"object","properties":{"descriptor":{"const":"Runs from a cloud computer"},"strength":{"const":"unconfirmed"},"howWeKnow":{"const":"Unconfirmed"}},"required":["descriptor","strength","howWeKnow"],"description":"Beside an agent verdict only: the session runs from a data-centre address with cloud-machine signs. A descriptor, never a name."},"kind":{"type":["string","null"],"enum":["operator","browser-extension","agentic-browser","framework","agent-framework","automation-framework",null],"description":"automation-framework (Playwright, Puppeteer, Selenium) is named only beside an agent verdict in a WebDriver page; it names the framework, never an AI model or agent category."},"observedAt":{"type":["number","null"]},"validUntil":{"type":["number","null"],"description":"Signature expiry; signed attribution lapses then."},"evidence":{"type":"array","items":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["web-bot-auth","page-marker","framework-artifact"]},"operatorType":{"type":"string","enum":["direct","intermediary"]}}}},"note":{"type":"string"}},"required":["strength","controller","operator","operatorType","cloudRun","kind","observedAt","validUntil","evidence","note"],"description":"Optional, separate from the verdict and never changes it. Null when no controller evidence exists and the verdict is not agent; marker absence never means human. A signature identifies the operator, not the user or AI model. No confidence values."},"ForwardedRequest":{"type":"object","properties":{"method":{"type":"string","enum":["GET","HEAD","POST","PUT","PATCH","DELETE","OPTIONS"]},"url":{"type":"string","format":"uri","maxLength":2048},"headers":{"type":"object","maxProperties":16,"additionalProperties":{"type":"string","maxLength":8192}},"path":{"type":"string","pattern":"^/","maxLength":2048,"description":"The request path (re-templated by Telltale; no query). With method alone (no url or headers) only the page-request check runs."},"correlation":{"type":"string","pattern":"^[A-Za-z0-9_-]{16}$","description":"The X-Observatory-Request header the browser SDK adds with requestCorrelation: true."}},"required":["method"],"description":"Your incoming request as the agent sent it: an allowed Site origin URL plus signature, signature-input, signature-agent and any other covered headers. Never forward cookie or authorization; they are rejected."},"Identity":{"type":"object","properties":{"userId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"accountId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"verified":{"type":"boolean"}},"required":["userId","accountId","verified"]},"Policy":{"type":"object","properties":{"human":{"type":"string","enum":["allow","review","deny"]},"agent":{"type":"string","enum":["allow","review","deny"]},"unknown":{"type":"string","enum":["allow","review","deny"]},"stale":{"type":"string","enum":["allow","review","deny"]},"maxAgeMs":{"type":"integer","minimum":1000,"maximum":300000}},"required":["human","agent","unknown","stale","maxAgeMs"]},"EdgeEvidenceInput":{"type":"object","properties":{"cloudflare":{"type":"object","properties":{"botScore":{"type":"integer","minimum":0,"maximum":99,"description":"request.cf.botManagement.score (1 automated, 2-29 likely automated, 30-99 likely human, 0 not scored)."},"verifiedBot":{"type":"boolean"},"verifiedBotCategory":{"type":"string","maxLength":64},"signedAgent":{"type":"boolean","description":"request.cf.botManagement.signedAgent: Cloudflare verified a Web Bot Auth signature from an agent it approved."},"signatureAgent":{"type":"string","format":"uri","maxLength":256,"description":"The https origin named by the Signature-Agent header, when signedAgent is true."},"ja4":{"type":"string","pattern":"^[tqd][0-9a-z]{9}_[0-9a-f]{12}_[0-9a-f]{12}$"},"detectionIds":{"type":"array","maxItems":32,"items":{"type":"integer","minimum":0}},"jsDetectionPassed":{"type":"boolean"},"staticResource":{"type":"boolean"}},"required":[],"additionalProperties":false,"minProperties":1},"fingerprint":{"type":"object","properties":{"bot":{"type":"string","enum":["not_detected","notDetected","good","bad"]},"botType":{"type":"string","maxLength":64},"tampering":{"type":"boolean"},"anomalyScore":{"type":"number","minimum":0,"maximum":1},"virtualMachine":{"type":"boolean"},"developerTools":{"type":"boolean"},"emulator":{"type":"boolean"},"vpn":{"type":"boolean"},"proxy":{"type":"boolean"},"incognito":{"type":"boolean"}},"required":[],"additionalProperties":false,"minProperties":1,"description":"Fingerprint Smart Signals results only. visitorId, linkedId, requestId and IP data are refused."}},"required":[],"additionalProperties":false,"minProperties":1,"description":"Outside evidence from your own edge or providers (/docs/edge-evidence). Server key only, never from a browser. Each source is kept separate and labelled; edge evidence on its own may lead to deny only for a Cloudflare-verified signed agent, otherwise to review at most. Nothing here can make a session human."},"EdgeEvidence":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["cloudflare","fingerprint"]},"label":{"type":"string"},"reportedBy":{"const":"customer-server"},"fields":{"type":"object"},"signals":{"type":"array","items":{"type":"object","properties":{"signal":{"type":"string"},"value":{},"strength":{"type":"string","enum":["edge-verified-signature","edge-verified-bot","vendor-heuristic","context"]},"mayLeadTo":{"type":"string","enum":["nothing","review","deny"]},"note":{"type":"string"}},"required":["signal","value","strength","mayLeadTo","note"]}},"mayLeadTo":{"type":"string","enum":["nothing","review","deny"]}},"required":["source","label","reportedBy","fields","signals","mayLeadTo"]}},"mayLeadTo":{"type":"string","enum":["nothing","review","deny"],"description":"The most this evidence may lead to: deny only for a Cloudflare-verified signed agent, review for heuristics and verified bots, nothing for context."},"appliedToDecision":{"type":"boolean","description":"Whether this evidence changed decision. It only ever raises it: allow to review, or to deny only when mayLeadTo is deny, and never past the action’s AI agent rule. It never changes the verdict and is never chargeable on its own."},"raisedFrom":{"type":"string","enum":["allow","review"],"description":"Present when appliedToDecision is true: the decision before the edge evidence."},"note":{"type":"string"}},"required":["sources","mayLeadTo","appliedToDecision","raisedFrom","note"],"description":"Present only when evidence was sent: what your edge reported, labelled per source with a strength. Recorded with the decision."},"Verification":{"type":"object","properties":{"actionVerdict":{"type":"string","enum":["human","agent","unknown"],"description":"The stricter verdict used for protection and billing. Enforce decision; analytics-only AI agent does not select the AI agent rule."},"agentEvidence":{"type":["string","null"],"enum":["verified","automation-trace","injected-input","sustained-behaviour","behaviour-change","behaviour-corroborated","cursor-driven","behaviour",null],"description":"What backs an AI agent actionVerdict: verified (a valid Web Bot Auth signature), automation-trace (webdriver flag, automation framework or headless browser), injected-input (keys with no hardware delay, on Chromium), sustained-behaviour (a consistent behaviour reading over about 20 s of activity, at least 10 input events, no Human reading in between, two independent behaviour signals), behaviour-change (after a Human reading, the same sustained evidence with physical-device input seen to stop: a handover), behaviour-corroborated (two or more independent behaviour families, keyboard timing, pointer and clicks, text insertion, agree on one page and the session shows no physical-device input) or behaviour (behaviour signals only). High confidence (every value but behaviour) follows the AI agent rule, Deny included, and is chargeable; medium confidence (behaviour) is held for review by default (the site owner may block it) and never charged. null when actionVerdict is not agent."},"billable":{"type":"boolean","description":"Whether this reading is chargeable (counts toward billing): an AI agent actionVerdict at high confidence. Medium confidence (behaviour) is never chargeable."},"heldInsteadOfDeny":{"const":true,"description":"Present when the AI agent rule said Deny but the verdict is medium confidence (behaviour alone) and the site holds medium confidence for review (the default), so decision is review."},"confidence":{"type":["string","null"],"enum":["high","medium","low",null],"description":"Strength band from agentEvidence, never a probability: high (verified, automation-trace, injected-input, sustained-behaviour, behaviour-change, behaviour-corroborated) or medium (behaviour). low is reserved. null when actionVerdict is not agent."},"behaviourOnlyDenyEnabled":{"type":"boolean","description":"Present for a medium-confidence AI agent verdict (agentEvidence behaviour): false when it was held for review (the default), true when the site blocks medium confidence so the AI agent rule applied in full."},"behaviourOnlyReason":{"type":"string","description":"Plain sentence saying which applied to a medium-confidence AI agent verdict."},"stepUp":{"$ref":"#/components/schemas/StepUpHint","description":"Present when decision is review: settle the hold with your own check and POST its result to report, or leave it to the hosted queue."},"analyticsOnly":{"type":"boolean","description":"Analytics-only: true when the evidence is enough to count the session as AI agent in reports but not to act on, so actionVerdict (used for protection and billing) stays unknown."},"sessionId":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"verdict":{"type":"string","enum":["human","agent","unknown"]},"score":{"type":"integer","enum":[1,50,99],"description":"Categorical evidence score: human=1, unknown=50, agent=99. Not a probability."},"reasons":{"type":"array","items":{"type":"string"}},"basis":{"type":"string"},"driver":{"type":["string","null"],"description":"Legacy label derived from attribution: the attributed controller, Unattributed automation for an agent verdict without naming evidence, or null. Prefer attribution."},"attribution":{"anyOf":[{"$ref":"#/components/schemas/Attribution"},{"type":"null"}]},"pageMarkers":{"type":"array","description":"Diagnostic only, not attribution: renamable page markers seen in the evidence window, whatever the verdict. attribution names a marker only beside an agent verdict.","items":{"type":"object","properties":{"type":{"type":"string","enum":["page-marker"]},"marker":{"type":"string"},"controller":{"type":"string"},"registryName":{"type":["string","null"]},"provenance":{"type":"string"},"observedAt":{"type":["integer","null"]}},"required":["type","marker","controller","registryName","provenance","observedAt"]}},"crawlerIdentity":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"operator":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string","enum":["verified","declared-contradicted","declared-unverified"]},"statusLabel":{"type":"string","enum":["Checked","Claim didn't check out","Couldn't check"]},"verified":{"type":"boolean"}},"required":["id","name","operator","kind","status","verified"],"description":"Present only when the request declared a known crawler or fetcher. A claim never changes the verdict."},"signatureCheck":{"type":"object","properties":{"status":{"type":"string","enum":["verified","absent","malformed","insecure-transport","not-web-bot-auth","missing-parameter","unsupported-algorithm","target-not-covered","signature-agent-missing","signature-agent-not-covered","unsupported-discovery","unsupported-component","missing-component","expired","not-yet-valid","unreviewed-operator","directory-unavailable","unknown-key","expired-key","invalid-signature","replayed"]},"operator":{"type":["string","null"]},"operatorType":{"type":"string","enum":["direct","intermediary"]}},"required":["status","operator"],"description":"Present only when request headers were forwarded. Failure or absence is not evidence of a human."},"warnings":{"type":"array","items":{"type":"string","enum":["policy_missing"]},"description":"Present only when there is something to fix. policy_missing: no policy is saved for this actionId, so the default policy (human allow, agent review, unknown allow, stale deny) decided; the Observatory-Warning response header says the same."},"declaredFetcher":{"type":"object","properties":{"id":{"type":"string","enum":["chatgpt-user","claude-user","perplexity-user","google-gemininotebook"]},"name":{"type":"string"},"operator":{"type":"string"},"status":{"type":"string","enum":["verified","declared-contradicted","declared-unverified"],"description":"verified: the request address is in the operator's published list. declared-contradicted (\"Claim didn't check out\"): the list was fresh and the address is outside it. declared-unverified (\"Couldn't check\"): the list was stale or unavailable. None of them changes the verdict."}},"required":["id","name","operator","status"],"description":"Present only when the collector or a forwarded request declared a user-triggered fetcher (ChatGPT-User, Claude-User, Perplexity-User) in its user agent. IP ranges are not verified, so it is a claim: it never changes the verdict, names a controller or selects an agent category."},"declaredAgent":{"type":"object","properties":{"id":{"type":"string","enum":["nova-act"]},"controller":{"type":"string"},"operator":{"type":"string"},"basis":{"type":"string","enum":["vendor-documented"]},"sourceStrength":{"type":"string","enum":["medium"]},"spoofable":{"const":true}},"required":["id","controller","operator","basis","sourceStrength","spoofable"],"description":"Present only when the collector or a forwarded request carried a self-declared agent token in its user agent (Agent-NovaAct/). Anyone can send or remove it: it never changes the verdict or selects a usage category, and it names a controller (strength declared) only beside an agent verdict. The user-agent string is never returned or stored."},"observedAt":{"type":"number","description":"Unix epoch milliseconds."},"stale":{"type":"boolean"},"decision":{"type":"string","enum":["allow","review","deny"]},"policy":{"$ref":"#/components/schemas/Policy"},"identity":{"anyOf":[{"$ref":"#/components/schemas/Identity"},{"type":"null"}]},"decisionId":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$","description":"The persisted decision. Use it to report the outcome, request review or read the review state."},"purpose":{"type":"string","enum":["protect","display"]},"targetId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"identityMatched":{"type":"boolean","description":"Present when you sent userId or accountId and the session has a server-associated identity: whether they match."},"review":{"type":"object","properties":{"status":{"type":"string","enum":["pending"]}},"required":["status"],"description":"Present when the decision is review: the action is waiting in the hosted review queue."},"edgeEvidence":{"$ref":"#/components/schemas/EdgeEvidence"},"note":{"type":"string"}},"required":["sessionId","actionId","verdict","score","reasons","observedAt","stale","decision","policy"]},"Decision":{"type":"object","properties":{"decisionId":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"sessionId":{"type":"string"},"targetId":{"type":["string","null"]},"decision":{"type":"string","enum":["allow","review","deny"]},"purpose":{"type":"string","enum":["protect","display"]},"verdict":{"type":"string","enum":["human","agent","unknown"]},"agentEvidence":{"type":["string","null"],"enum":["verified","automation-trace","injected-input","sustained-behaviour","behaviour-change","behaviour-corroborated","cursor-driven","behaviour",null]},"heldInsteadOfDeny":{"const":true},"confidence":{"type":["string","null"],"enum":["high","medium","low",null]},"behaviourOnlyDenyEnabled":{"type":"boolean"},"behaviourOnlyReason":{"type":["string","null"]},"stepUp":{"anyOf":[{"$ref":"#/components/schemas/StepUpHint"},{"type":"null"}]},"created":{"type":"number","description":"Unix epoch milliseconds."},"outcome":{"anyOf":[{"type":"object","properties":{"outcome":{"type":"string","enum":["performed","blocked","failed"]},"reason":{"type":["string","null"]},"at":{"type":"number","description":"Unix epoch milliseconds."}},"required":["outcome","reason","at"]},{"type":"null"}]},"review":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","rejected","cancelled"]},"resolvedAt":{"type":["integer","null"]},"expiresAt":{"type":["integer","null"]},"redeemedAt":{"type":["integer","null"]},"via":{"const":"step-up","description":"Present when your step-up report settled it."},"method":{"type":"string"}},"required":["status","resolvedAt","expiresAt","redeemedAt"]},{"type":"null"}]},"approvalToken":{"type":"string","description":"Present only while an approval can still be redeemed (approved, unexpired, unused). An approval is evidence, not authorization."}},"required":["decisionId","actionId","sessionId","decision","purpose","verdict","created"]},"StepUpHint":{"type":"object","properties":{"reason":{"type":"string","enum":["behaviour_only_agent","edge_evidence","policy","backend"],"description":"behaviour_only_agent: the AI agent rule said Deny but the verdict rests on behaviour alone. edge_evidence: evidence your edge reported raised the decision to review. policy: the rule says Hold for review. backend: your backend sent the check to review."},"expiresAt":{"type":"number","description":"The step-up result must arrive before this (15 minutes after the hold)."},"report":{"type":"string","description":"Where to POST the step-up result."}},"required":["reason","expiresAt","report"]},"StepUpResult":{"type":"object","properties":{"decisionId":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"targetId":{"type":["string","null"]},"sessionId":{"type":"string"},"stepUp":{"type":"string","enum":["passed","failed"]},"method":{"type":"string","enum":["passkey","mfa","turnstile","own_approval","other"]},"proceed":{"type":"boolean","description":"true only for a pass: the hold is approved and used once. Still run your own authorization before the write."},"resolvedAt":{"type":"number","description":"Unix epoch milliseconds."},"replayed":{"type":"boolean"},"note":{"type":"string"}},"required":["decisionId","actionId","targetId","sessionId","stepUp","method","proceed","resolvedAt","replayed","note"]},"Redemption":{"type":"object","properties":{"redeemed":{"const":true},"replayed":{"type":"boolean"},"decisionId":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"targetId":{"type":["string","null"]},"sessionId":{"type":"string"},"approvedAt":{"type":"number","description":"Unix epoch milliseconds."},"approvedBy":{"type":["string","null"]},"reason":{"type":"string"},"note":{"type":"string"}},"required":["redeemed","replayed","decisionId","actionId","targetId","sessionId","approvedAt","approvedBy","reason","note"]},"MeteredUsage":{"type":"object","properties":{"analyticsVerdict":{"type":"string","enum":["human","agent","unknown"]},"analyticsOnly":{"type":"boolean","description":"Analytics-only: analytics counted the session as AI agent, but this usage keeps the stricter verdict (verdict) for billing."},"id":{"type":"string","pattern":"^meter_[a-f0-9]{64}$"},"siteId":{"type":"string"},"sessionId":{"type":"string"},"userId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"accountId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"kind":{"const":"work_completed"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"executionId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Optional stored execution ID. Must match this site, session, trusted account and action."},"units":{"type":"integer","minimum":1,"maximum":1000000},"source":{"const":"server-reported"},"outcomeBasis":{"const":"application-server-reported"},"verdict":{"type":"string","enum":["human","agent","unknown"]},"basis":{"type":"string"},"accountResponse":{"type":"string","enum":["default","flagged","agent_seat"],"description":"Account response in force when the usage was recorded."},"via":{"type":"string","enum":["tool","ui","unknown"],"description":"How the work reached your application, derived when it was recorded: tool (a reviewed tool or workflow with the same name as actionId ran in this session within 15 minutes before), ui (no tool ran in that window and UI interactions were received) or unknown (anything else; never counted as ui). Says nothing about who drove it; verdict does."},"observedAt":{"type":"number","description":"Unix epoch milliseconds."},"created":{"type":"number","description":"Unix epoch milliseconds."},"replayed":{"type":"boolean"}},"required":["id","siteId","sessionId","userId","accountId","kind","actionId","units","source","verdict","basis","observedAt","created","replayed"]},"AgentUsageUnits":{"type":"object","properties":{"toolExecutions":{"type":"integer","minimum":0},"sessionMinutes":{"type":"integer","minimum":0}},"required":["toolExecutions","sessionMinutes"]},"AgentUsage":{"type":"object","properties":{"siteId":{"type":"string"},"since":{"type":"number","description":"Unix epoch milliseconds."},"until":{"type":"number","description":"Unix epoch milliseconds."},"asOf":{"type":"number","description":"Unix epoch milliseconds."},"window":{"type":"string","description":"The period in words, for example \"Oct 21 – Oct 27 UTC (last 7 days)\"."},"windowDays":{"type":"number","description":"Length of the requested period in days."},"accountId":{"type":"string","description":"Present when filtered to one account."},"clampedToRetention":{"type":"boolean","description":"true when from was earlier than the Site usage retention (90 days by default, up to 400) and was moved forward."},"periodBounds":{"type":"string","const":"[since, until)","description":"The period is half-open: a unit exactly at until belongs to the next period."},"coverageStart":{"type":"number","description":"Earliest instant this report can cover: the later of since and the Site usage retention floor. Chargeable units are never removed by count caps."},"oldestRetainedAt":{"type":["integer","null"],"description":"Timestamp of the oldest agent unit this Site still retains, in any period; null when none."},"page":{"type":"object","properties":{"limit":{"type":"integer"},"total":{"type":"integer","description":"Account rows in the whole period."},"nextCursor":{"type":["string","null"],"description":"Continues the same period and account in stable (accountId, source, category) order. Sending it with a different from, to or accountId is rejected with 400."}},"required":["limit","total","nextCursor"]},"totals":{"type":"object","description":"toolExecutions and sessionMinutes count every agent unit shown; metered and included count chargeable units only; billable and behaviourOnly split the units by whether they count toward billing."},"categoriesUsed":{"type":"integer"},"categories":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","enum":["computer_use","browser_use","coding_agent","crawler","unattributed"]},"label":{"type":"string"},"mode":{"type":"string","enum":["metered","included"]},"toolExecutions":{"type":"integer"},"sessionMinutes":{"type":"integer"},"metered":{"$ref":"#/components/schemas/AgentUsageUnits"},"included":{"$ref":"#/components/schemas/AgentUsageUnits"},"billable":{"$ref":"#/components/schemas/AgentUsageUnits","description":"Units that count toward billing: metered plus included."},"behaviourOnly":{"$ref":"#/components/schemas/AgentUsageUnits","description":"AI agent units whose verdict rests on behaviour alone (no verified identity or automation trace): shown, never charged. billable plus behaviourOnly equals the unit totals."},"tools":{"type":"integer"},"accounts":{"type":"integer"}},"required":["category","label","mode","toolExecutions","sessionMinutes","metered","included","billable","behaviourOnly","tools","accounts"]}},"accounts":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":["string","null"]},"source":{"type":"string","enum":["server-associated","browser-supplied","unattributed","not-recorded"]},"category":{"type":"string","enum":["computer_use","browser_use","coding_agent","crawler","unattributed"]},"currentResponse":{"type":"string","enum":["default","flagged","agent_seat"],"description":"Current per-account response setting."},"toolExecutions":{"type":"integer"},"sessionMinutes":{"type":"integer"},"metered":{"$ref":"#/components/schemas/AgentUsageUnits"},"included":{"$ref":"#/components/schemas/AgentUsageUnits"},"billable":{"$ref":"#/components/schemas/AgentUsageUnits"},"behaviourOnly":{"$ref":"#/components/schemas/AgentUsageUnits"},"byResponse":{"type":"object","description":"Units by the account response snapshotted at ingestion (default, flagged, agent_seat); older units without a snapshot are not counted here."}},"required":["accountId","source","category","toolExecutions","sessionMinutes","metered","included"]}},"agentsUsed":{"type":"integer","minimum":0,"description":"Distinct named controllers in the period."},"agents":{"type":"array","description":"One row per controller and attribution strength. A controller is named only from signed or weak-marker attribution, or a crawler verified against the operator IP-range list (ip-verified), captured at ingestion; all other agent usage is one row with controller null and strength unattributed.","items":{"type":"object","properties":{"controller":{"type":["string","null"]},"strength":{"type":"string","enum":["signed","weak-marker","ip-verified","unattributed"]},"category":{"type":"string","enum":["computer_use","browser_use","coding_agent","crawler","unattributed"]},"toolExecutions":{"type":"integer"},"sessionMinutes":{"type":"integer"},"metered":{"$ref":"#/components/schemas/AgentUsageUnits"},"included":{"$ref":"#/components/schemas/AgentUsageUnits"},"billable":{"$ref":"#/components/schemas/AgentUsageUnits"},"behaviourOnly":{"$ref":"#/components/schemas/AgentUsageUnits"},"lastAt":{"type":["number","null"]}},"required":["controller","strength","category","toolExecutions","sessionMinutes","metered","included","billable","behaviourOnly","lastAt"]}},"coverage":{"type":"string"},"seriesBucket":{"type":"string","description":"Present with series: UTC day or ISO week (Monday 00:00 UTC)."},"series":{"type":"array","description":"Present with series=day|week: half-open buckets [start, end) that partition the period and add up to totals.","items":{"type":"object","properties":{"start":{"type":"number","description":"Unix epoch milliseconds."},"end":{"type":"number","description":"Unix epoch milliseconds."},"toolExecutions":{"type":"integer","minimum":0},"sessionMinutes":{"type":"integer","minimum":0},"metered":{"$ref":"#/components/schemas/AgentUsageUnits"},"included":{"$ref":"#/components/schemas/AgentUsageUnits"}},"required":["start","end","toolExecutions","sessionMinutes","metered","included"]}},"previous":{"type":["object","null"],"description":"Present with compare=previous: the period of the same length ending at since (clamped to retention), or null when it lies entirely outside retention.","properties":{"since":{"type":"number","description":"Unix epoch milliseconds."},"until":{"type":"number","description":"Unix epoch milliseconds."},"clampedToRetention":{"type":"boolean"},"totals":{"type":"object"}}},"change":{"type":"object","description":"Present with compare=previous when a previous period exists: unit count differences.","properties":{"toolExecutions":{"type":"integer"},"sessionMinutes":{"type":"integer"}}}},"required":["siteId","since","asOf","categories","accounts","agents","agentsUsed"],"description":"Retained seven-day agent usage. Category and account are snapshots taken at ingestion; metered and included follow the current billing mode of each category (mode), for every retained chargeable unit; behaviour-only AI agent units are counted in the unit totals and behaviourOnly, never in metered or included. Unattributed means agent evidence without a named controller. Not an invoice."},"Activity":{"type":"object","properties":{"siteId":{"type":"string"},"since":{"type":"number","description":"Unix epoch milliseconds."},"until":{"type":"number","description":"Unix epoch milliseconds."},"periodBounds":{"const":"[since, until)"},"window":{"type":"string","description":"The period in words: last 7 days: 7 whole UTC days ending with today, or from and to as given (UTC)."},"bucket":{"type":"string","enum":["UTC day","ISO week (Monday 00:00 UTC)"]},"clampedToRetention":{"type":"boolean"},"totals":{"type":"object","properties":{"minutes":{"type":"object","properties":{"human":{"type":"integer"},"agent":{"type":"integer"},"unknown":{"type":"integer"},"mixed":{"type":"integer"},"total":{"type":"integer"},"percent":{"type":"object","description":"Shares of minutes rounded to one decimal (largest remainder), not probabilities."}},"required":["human","agent","unknown","mixed","total","percent"]}},"required":["minutes"]},"series":{"type":"array","items":{"type":"object","properties":{"start":{"type":"number","description":"Unix epoch milliseconds."},"end":{"type":"number","description":"Unix epoch milliseconds."},"minutes":{"type":"object","properties":{"human":{"type":"integer"},"agent":{"type":"integer"},"unknown":{"type":"integer"},"mixed":{"type":"integer"},"total":{"type":"integer"}},"required":["human","agent","unknown","mixed","total"]},"sessions":{"type":["object","null"],"description":"Whole-UTC-day session totals by last-observed verdict that this bucket overlaps; null where no daily total exists.","properties":{"human":{"type":"integer"},"agent":{"type":"integer"},"unknown":{"type":"integer"},"total":{"type":"integer"}}}},"required":["start","end","minutes","sessions"]}},"sessionRollupStart":{"type":["number","null"]},"coverage":{"type":"string"}},"required":["siteId","since","until","periodBounds","window","bucket","clampedToRetention","totals","series","sessionRollupStart","coverage"]},"WebhookEvent":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["session.verdict_changed","session.identified","usage.recorded","agent_usage.recorded","agent_usage.summary","account.seat_changed","tool.completed","tool.suspect","action.schema_changed","protection.decision","review.resolved","report.weekly","alert.threshold_crossed","account.agent_usage_started","account.agent_usage_threshold","account.agent_calls_failing","agent.first_seen","agent_call.recorded","agent_call.errors","agent_call.slow","agent_call.new_account","agent_client.new","issue.regressed","agent_feedback.received","webhook.test"]},"createdAt":{"type":"string","format":"date-time"},"data":{"type":"object","description":"Depends on type (see the Webhooks guide). agent_usage.recorded data has usageId, unit, sessionId, accountId, accountSource, category, billingMode, accountResponse, units, meteredUnits, toolId and at; when a stronger account source arrives later in the same session-minute, the same usageId is sent again with revised: true, previousAccountId and previousAccountSource. That is a correction of one unit, not a second unit. agent_usage.summary data carries window, the period in words."}},"required":["id","type","createdAt","data"]},"UsageLedger":{"type":"object","properties":{"object":{"const":"usage_ledger"},"siteId":{"type":"string"},"view":{"type":"string","enum":["days","accounts","entries"]},"since":{"type":"number","description":"Unix epoch milliseconds."},"until":{"type":"number","description":"Unix epoch milliseconds."},"period":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"halfOpen":{"const":true}},"required":["from","to","halfOpen"]},"clamped":{"type":"boolean","description":"true when from was moved forward to what the Site still keeps."},"coverage":{"type":"object","properties":{"countsFrom":{"type":"string","description":"Oldest UTC day with day counts (YYYY-MM-DD)."},"entriesFrom":{"type":"string","format":"date-time","description":"Oldest entry still kept (the Site usage retention)."}},"required":["countsFrom","entriesFrom"]},"classes":{"type":"array","items":{"type":"string","enum":["billable","behaviour","human","unknown"]}},"note":{"type":"string"},"totals":{"$ref":"#/components/schemas/UsageLedgerCounts","description":"days and accounts views: the whole period, all pages."},"days":{"type":"array","description":"days view: one row per UTC day, account and user.","items":{"$ref":"#/components/schemas/UsageLedgerCounts","properties":{"day":{"type":"string","description":"YYYY-MM-DD (UTC)."},"accountId":{"type":["string","null"]},"userId":{"type":["string","null"]},"adjustments":{"type":"integer","description":"Adjustment entries posted for this row."},"erased":{"const":true,"description":"Present when the subject was erased: the counts stay, the IDs are gone."}}}},"accounts":{"type":"array","description":"accounts view: one row per account, most chargeable units first.","items":{"$ref":"#/components/schemas/UsageLedgerCounts","properties":{"accountId":{"type":["string","null"]},"users":{"type":"integer","description":"Distinct users in the period."},"firstDay":{"type":"string"},"lastDay":{"type":"string"}}}},"entries":{"type":"array","description":"entries view: the entries, oldest first.","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["unit","adjustment","route_call","erasure"],"description":"unit: one POST /api/v1/usage record. adjustment: a later correction (class or units changed; the original stays). route_call: an approved-route call (MCP, API or page tool). erasure: a subject was erased (no identity)."},"usageId":{"type":["string","null"]},"at":{"type":["number","null"]},"postedAt":{"type":"number","description":"Unix epoch milliseconds."},"day":{"type":"string"},"accountId":{"type":["string","null"]},"userId":{"type":["string","null"]},"actionId":{"type":["string","null"]},"units":{"type":"integer"},"class":{"type":["string","null"],"enum":["billable","behaviour","human","unknown",null]},"fromClass":{"type":"string","description":"adjustment only: the class before."},"reason":{"type":"string"},"counts":{"type":"object","description":"Units per class for this entry (an adjustment is negative in its old class)."},"evidence":{"type":["object","null"],"description":"Session IDs, the verdict basis, evidenceClass (verified-identity, automation-trace, injected-input, sustained-behaviour, behaviour-change, behaviour-corroborated, cursor-driven, behaviour-only, human, unknown) and classLabel."},"idempotencyKey":{"type":"string","description":"tt_ plus the entry ID: use it as the idempotency key when you forward the entry to your billing system."}},"required":["id","type","postedAt","day","units","counts","idempotencyKey"]}},"page":{"type":"object","properties":{"limit":{"type":"integer"},"total":{"type":"integer","description":"days and accounts views only."},"nextCursor":{"type":["string","null"],"description":"Send as cursor to read the next page; null on the last page."}},"required":["limit","nextCursor"]}},"required":["object","siteId","view","since","until","period","coverage","classes","page"]},"UsageLedgerCounts":{"type":"object","properties":{"billableUnits":{"type":"integer","description":"Chargeable: high-confidence AI agent units."},"behaviourUnits":{"type":"integer","description":"Medium-confidence AI agent units (one behaviour family): shown, never chargeable."},"humanUnits":{"type":"integer"},"unknownUnits":{"type":"integer"},"routeCalls":{"type":"integer","description":"Approved-route calls (MCP, API, page tools)."},"routeBillableCalls":{"type":"integer","description":"Approved-route calls for an account your server named: chargeable."}},"required":["billableUnits","behaviourUnits","humanUnits","unknownUnits","routeCalls","routeBillableCalls"]},"WorkflowExport":{"type":"object","properties":{"version":{"const":"1"},"siteId":{"type":"string"},"workflows":{"type":"array","items":{"type":"object","description":"Saved workflow definition, including steps, signature, approval status and reviewed input labels. Session evidence is excluded. The definition may be disabled."}}},"required":["version","siteId","workflows"]},"ChannelDecisionState":{"type":"object","properties":{"decisionId":{"type":"string"},"actionId":{"type":"string","description":"The tool name (MCP) or METHOD /route (API) the decision is for."},"decision":{"type":"string","enum":["allow","review","deny"],"description":"The answer decide gave; a hold is review."},"review":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"pending while waiting for the dashboard or your step-up; approved or rejected once settled."},"expiresAt":{"type":["integer","null"],"description":"Unix epoch ms: the hold, or an approval, lapses after this."},"redeemed":{"type":"boolean","description":"true once an approval was used by a decide call with approvedDecisionId."}},"required":["status","expiresAt","redeemed"]},{"type":"null"}],"description":"null when the decision was not a hold."}},"required":["decisionId","actionId","decision","review"],"description":"Poll this while a call is held (decision review). review.status is pending while waiting, then approved (the next decide with approvedDecisionId is allowed once, within 15 minutes; redeemed turns true when it is used) or rejected. decision stays the original answer (review); approval shows in review, not in decision. An unknown, expired or other Site’s decisionId is 404."},"ChannelStepUpRequest":{"type":"object","properties":{"result":{"type":"string","enum":["passed","failed"]},"method":{"type":"string","enum":["passkey","mfa","turnstile","own_approval","other"]}},"required":["result"],"additionalProperties":false},"ChannelStepUpResult":{"type":"object","properties":{"decisionId":{"type":"string"},"status":{"type":"string","enum":["approved","rejected"]},"expiresAt":{"type":["integer","null"]}},"required":["decisionId","status","expiresAt"]},"SessionLookup":{"type":"object","properties":{"sessionId":{"type":"string","pattern":"^session_[A-Za-z0-9_-]{8,64}$"},"status":{"type":"string","enum":["active","ended","consent_withdrawn"]},"started":{"type":"number","description":"Unix epoch milliseconds."},"lastActivity":{"type":"number","description":"Unix epoch milliseconds."},"ended":{"type":["number","null"],"description":"When the session expired (ended) or consent was withdrawn, if recorded; null while active."},"verdict":{"type":"string","enum":["human","agent","unknown"],"description":"While active, the reading now (as /api/v1/verify computes it); once ended, the reading at the last activity, which is final; unknown after consent withdrawal. Unknown is not human."},"actionVerdict":{"type":"string","enum":["human","agent","unknown"],"description":"The stricter verdict used for protection and billing. It differs from verdict only when the session is analytics-only: it counts as AI agent in reports, but the evidence is not strong enough to act on, so actionVerdict stays unknown."},"verdictAt":{"type":["number","null"],"description":"The time the verdict was read at (now, or the last activity); null after withdrawal."},"agentEvidence":{"type":["string","null"],"enum":["verified","automation-trace","injected-input","sustained-behaviour","behaviour-change","behaviour-corroborated","cursor-driven","behaviour",null],"description":"What backs an AI agent action verdict; null otherwise. It sets the confidence band exactly as on verify: every value but behaviour is high confidence (follows the AI agent rule, Deny included, and is chargeable); behaviour (one behaviour family) is medium confidence (held for review by default, never chargeable)."},"agentEvidenceLabel":{"type":"string","enum":["Verified","Strong evidence","Behaviour","Unknown"],"description":"The label the dashboard shows for agentEvidence: Verified and Strong evidence are high confidence, Behaviour is medium confidence, Unknown means no AI agent action verdict."},"everAgent":{"type":["boolean","null"],"description":"Whether the action verdict ever read AI agent in this session; null after consent withdrawal (withheld)."},"agentFirstSeen":{"type":["number","null"]},"agentLastSeen":{"type":["number","null"]},"agent":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"operator":{"type":["string","null"]},"howWeKnow":{"type":"object","properties":{"strength":{"type":"string"},"label":{"type":"string"}},"required":["strength","label"]}},"required":["name","operator","howWeKnow"]},{"type":"null"}],"description":"The named agent and how we know, only when evidence names one beside an AI agent verdict."},"identity":{"anyOf":[{"type":"object","properties":{"userId":{"type":["string","null"]},"accountId":{"type":["string","null"]},"verified":{"type":"boolean","description":"true: asserted by your server (identify); false: a browser claim."}},"required":["userId","accountId","verified"]},{"type":"null"}]},"detectorVersion":{"type":["string","null"],"description":"The classifier that produced the verdict (its name, such as heuristic-v4.33); null after withdrawal."},"reasons":{"type":"array","items":{"type":"string"}},"decisions":{"type":"array","maxItems":100,"items":{"type":"object","properties":{"decisionId":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"decision":{"type":"string","enum":["allow","review","deny"]},"purpose":{"type":"string","enum":["protect","display"]},"created":{"type":"number","description":"Unix epoch milliseconds."},"outcome":{"type":["string","null"],"enum":["performed","blocked","failed",null]},"review":{"type":["string","null"]}},"required":["decisionId","actionId","decision","purpose","created","outcome","review"]},"description":"Newest first, at most 100; GET /api/v1/decisions.json?sessionId= reads every one."},"decisionsComplete":{"type":"boolean"},"goals":{"type":"array","maxItems":12,"description":"What the session tried to do and whether it worked, one plain line each, newest first. Derived from tool runs, protected-action checks and their outcomes, work your server reported and refused tool calls; fixed templates, never page text. status is a category: succeeded, blocked, failed, waiting, abandoned or unconfirmed (no result reported, never counted as success).","items":{"type":"object","properties":{"actionId":{"type":"string"},"label":{"type":"string"},"status":{"type":"string","enum":["succeeded","blocked","failed","waiting","abandoned","unconfirmed"]},"cause":{"type":"string"},"line":{"type":"string"},"attempts":{"type":"integer"},"via":{"type":"array","items":{"type":"string","enum":["tool","protected","page","unknown"]}},"httpStatus":{"type":"integer"},"firstAt":{"type":"number","description":"Unix epoch milliseconds."},"lastAt":{"type":"number","description":"Unix epoch milliseconds."}},"required":["actionId","label","status","cause","line","attempts","via","firstAt","lastAt"]}},"goalSummary":{"type":["string","null"],"description":"The top line: the one goal, or how many were tried and how they ended."},"retainedUntil":{"type":"number","description":"After this time the lookup is 404: from the last activity, the plan’s history period for a session with AI agent activity (never less than the session setting), else the Site’s session retention (default 8 days); at most 8 days after consent withdrawal."}},"required":["sessionId","status","started","lastActivity","ended","verdict","actionVerdict","verdictAt","agentEvidence","agentEvidenceLabel","everAgent","agentFirstSeen","agentLastSeen","agent","identity","detectorVersion","reasons","decisions","decisionsComplete","goals","goalSummary","retainedUntil"],"additionalProperties":false},"CompatSessionUserIdentity":{"type":"object","properties":{"userId":{"type":"string","minLength":1,"pattern":"\\S"},"accountId":{"anyOf":[{"type":"string","minLength":1,"pattern":"\\S"},{"type":"null"}]},"verified":{"type":"boolean"}},"required":["userId","accountId","verified"],"additionalProperties":false},"CompatSessionAccountIdentity":{"type":"object","properties":{"userId":{"type":"null"},"accountId":{"type":"string","minLength":1,"pattern":"\\S"},"verified":{"type":"boolean"}},"required":["userId","accountId","verified"],"additionalProperties":false},"CompatSession":{"type":"object","properties":{"sessionId":{"type":"string"},"status":{"type":"string","enum":["active","ended"]},"identity":{"anyOf":[{"oneOf":[{"$ref":"#/components/schemas/CompatSessionUserIdentity"},{"$ref":"#/components/schemas/CompatSessionAccountIdentity"}]},{"type":"null"}]},"verdict":{"type":"string","enum":["human","agent","unknown"]}},"required":["sessionId","status","identity","verdict"],"additionalProperties":false,"description":"The v1 SessionResponse, exactly. A withdrawn session reads ended and unknown."},"BadRequestError":{"type":"object","properties":{"error":{"type":"string","enum":["bad_request"]}},"required":["error"],"additionalProperties":false,"description":"The request is invalid (invalid JSON, an unknown property, a missing or empty sessionToken, an invalid actionId or ID, or a body over 131072 bytes). Fix the request before sending it again."},"UnauthorizedError":{"type":"object","properties":{"error":{"type":"string","enum":["unauthorized"]}},"required":["error"],"additionalProperties":false,"description":"The server key is missing, invalid or not sent with the Bearer scheme, or the session token is invalid: malformed, over 250 characters, shaped like a key, or not signed by this collector. Check the key, then replace only the session token."},"ForbiddenError":{"type":"object","properties":{"error":{"type":"string","enum":["forbidden"]}},"required":["error"],"additionalProperties":false,"description":"The server key and session token belong to different Sites, or the call came from a browser page on the public collector origin."},"NotFoundError":{"type":"object","properties":{"error":{"type":"string","enum":["not_found"]}},"required":["error"],"additionalProperties":false,"description":"Unknown path or wrong method."},"SessionReinitRequiredError":{"type":"object","properties":{"error":{"type":"string","enum":["session_reinit_required"]}},"required":["error"],"additionalProperties":false,"description":"The session token was issued by this collector but can no longer be verified: its session expired, was revoked (for example on consent withdrawal) or was removed, or identify tried to replace an established identity. Start a new browser session and get a new token; do not resend the old one."},"RateLimitedError":{"type":"object","properties":{"error":{"type":"string","enum":["rate_limited"]}},"required":["error"],"additionalProperties":false,"description":"Too many requests from one client IP. Wait for Retry-After."},"InternalError":{"type":"object","properties":{"error":{"type":"string","enum":["internal_error"]}},"required":["error"],"additionalProperties":false,"description":"The collector could not complete the request. Fail the protected action and do not retry automatically."},"ConfigUnavailableError":{"type":"object","properties":{"error":{"type":"string","enum":["config_unavailable"]}},"required":["error"],"additionalProperties":false,"description":"Site configuration is temporarily unavailable."},"SessionUnavailableError":{"type":"object","properties":{"error":{"type":"string","enum":["session_unavailable"]}},"required":["error"],"additionalProperties":false,"description":"The browser session is temporarily unavailable."},"DiscoveryNotReadyError":{"type":"object","properties":{"error":{"type":"string","enum":["discovery_not_ready"]}},"required":["error"],"additionalProperties":false,"description":"Discovery is on, but nothing has been observed yet."},"DiscoveryUnavailableError":{"type":"object","properties":{"error":{"type":"string","enum":["discovery_unavailable"]}},"required":["error"],"additionalProperties":false,"description":"Discovery is off for this Site, or storage is busy."},"SessionNotFoundError":{"type":"object","properties":{"error":{"type":"string","enum":["session_not_found"]}},"required":["error"],"additionalProperties":false,"description":"The session is unknown, belongs to another Site, was erased or deleted, or is past its retention. Which one is never said."},"UnavailableBody":{"type":"object","description":"A temporary Site configuration or browser session failure; the code names what is unavailable. Only verify may retry (three attempts, 0-250 ms then 0-500 ms jitter).","oneOf":[{"$ref":"#/components/schemas/ConfigUnavailableError"},{"$ref":"#/components/schemas/SessionUnavailableError"}],"discriminator":{"propertyName":"error","mapping":{"config_unavailable":"#/components/schemas/ConfigUnavailableError","session_unavailable":"#/components/schemas/SessionUnavailableError"}}},"DiscoveryUnavailableBody":{"type":"object","description":"The discovered document is not ready or unavailable.","oneOf":[{"$ref":"#/components/schemas/DiscoveryNotReadyError"},{"$ref":"#/components/schemas/DiscoveryUnavailableError"}],"discriminator":{"propertyName":"error","mapping":{"discovery_not_ready":"#/components/schemas/DiscoveryNotReadyError","discovery_unavailable":"#/components/schemas/DiscoveryUnavailableError"}}}}},"webhooks":{"observatoryEvent":{"post":{"operationId":"receiveObservatoryEvent","summary":"Signed event delivered to your configured endpoint","description":"Standard Webhooks (standardwebhooks.com). webhook-signature lists space-separated v1,<base64 HMAC-SHA256> signatures of webhook-id.webhook-timestamp.body, keyed by the base64 bytes after whsec_; during a 24-hour rotation overlap there is one per live secret. Reject timestamps more than 5 minutes from now and deduplicate on webhook-id, which is stable across retries. Any 2xx acknowledges; other responses and timeouts retry up to five attempts with backoff. verifyWebhook in the server adapter (/adapters/v1/server.js) implements this check. Endpoints created before Standard Webhooks also receive the deprecated Observatory-Event-Id and Observatory-Signature headers until an owner turns them off.","security":[],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Event ID; equals the payload id."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"},"description":"Attempt time in Unix seconds."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]+=*( v1,[A-Za-z0-9+/]+=*)*$"}},{"name":"Observatory-Signature","in":"header","required":false,"deprecated":true,"schema":{"type":"string","pattern":"^t=[0-9]+,v1=[a-f0-9]{64}$"},"description":"Legacy hex HMAC of timestamp.body keyed by the full secret string; current secret only."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Delivery acknowledged"}}}}},"paths":{"/v1/verify":{"post":{"operationId":"compatVerify","summary":"Compatibility API (/v1) verification","description":"Uses the stricter actionVerdict from /api/v1/verify (analytics-only AI agent is excluded), returned in the v1 shape {verdict, score}. Scores are categorical (human 1, unknown 50, agent 99), never probabilities; unknown is not human. Failures are exactly {\"error\":\"<code>\"}. The request body is closed (sessionToken and actionId only); to forward Web Bot Auth headers use /api/v1/verify.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"pattern":"\\S","description":"Browser SDK session token. Missing, empty or non-string is 400 bad_request; malformed, over 250 characters, shaped like a key or not signed by this collector is 401 unauthorized; a token this collector issued whose session expired, was revoked or was removed is 409 session_reinit_required."},"actionId":{"type":"string","pattern":"^[a-z0-9_-]{1,64}$","description":"Lowercase on /v1; /api/v1 also accepts uppercase."}},"required":["sessionToken","actionId"],"additionalProperties":false,"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","actionId":"export_report"}}}}},"responses":{"200":{"description":"Verdict","content":{"application/json":{"schema":{"type":"object","properties":{"verdict":{"type":"string","enum":["human","agent","unknown"]},"score":{"type":"integer","enum":[1,50,99]}},"required":["verdict","score"],"additionalProperties":false}}}},"400":{"description":"The request is invalid (invalid JSON, an unknown property, a missing or empty sessionToken, an invalid actionId or ID, or a body over 131072 bytes). Fix the request before sending it again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"}}}},"401":{"description":"The server key is missing, invalid or not sent with the Bearer scheme, or the session token is invalid: malformed, over 250 characters, shaped like a key, or not signed by this collector. Check the key, then replace only the session token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}}},"403":{"description":"The server key and session token belong to different Sites, or the call came from a browser page on the public collector origin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Unknown path or wrong method.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The session token was issued by this collector but can no longer be verified: its session expired, was revoked (for example on consent withdrawal) or was removed, or identify tried to replace an established identity. Start a new browser session and get a new token; do not resend the old one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionReinitRequiredError"}}}},"429":{"description":"Too many requests from one client IP. Wait for Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitedError"}}},"headers":{"Retry-After":{"description":"Seconds until the collector admits requests again. Never replay the protected application action.","required":true,"schema":{"type":"string"}}}},"500":{"description":"The collector could not complete the request. Fail the protected action and do not retry automatically.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}},"503":{"description":"A temporary Site configuration or browser session failure; the code names what is unavailable. Only verify may retry (three attempts, 0-250 ms then 0-500 ms jitter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnavailableBody"}}}}}}},"/v1/identify":{"post":{"operationId":"compatIdentify","summary":"Compatibility API (/v1) server identity","description":"Associates server-verified userId and/or accountId (each at most 512 characters). Success is exactly {\"status\":\"accepted\"}. Replacing an established identity returns 409 session_reinit_required. No automatic retries. The request body is closed: sessionToken with userId (and optional accountId), or sessionToken with accountId alone.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"pattern":"\\S","description":"Browser SDK session token. Missing, empty or non-string is 400 bad_request; malformed, over 250 characters, shaped like a key or not signed by this collector is 401 unauthorized; a token this collector issued whose session expired, was revoked or was removed is 409 session_reinit_required."},"userId":{"type":"string","minLength":1,"maxLength":512,"pattern":"\\S"},"accountId":{"type":"string","minLength":1,"maxLength":512,"pattern":"\\S"}},"required":["sessionToken","userId"],"additionalProperties":false},{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"pattern":"\\S","description":"Browser SDK session token. Missing, empty or non-string is 400 bad_request; malformed, over 250 characters, shaped like a key or not signed by this collector is 401 unauthorized; a token this collector issued whose session expired, was revoked or was removed is 409 session_reinit_required."},"accountId":{"type":"string","minLength":1,"maxLength":512,"pattern":"\\S"}},"required":["sessionToken","accountId"],"additionalProperties":false}],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","userId":"usr_123","accountId":"acct_456"}}}}},"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"const":"accepted"}},"required":["status"],"additionalProperties":false}}}},"400":{"description":"The request is invalid (invalid JSON, an unknown property, a missing or empty sessionToken, an invalid actionId or ID, or a body over 131072 bytes). Fix the request before sending it again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"}}}},"401":{"description":"The server key is missing, invalid or not sent with the Bearer scheme, or the session token is invalid: malformed, over 250 characters, shaped like a key, or not signed by this collector. Check the key, then replace only the session token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}}},"403":{"description":"The server key and session token belong to different Sites, or the call came from a browser page on the public collector origin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Unknown path or wrong method.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The session token was issued by this collector but can no longer be verified: its session expired, was revoked (for example on consent withdrawal) or was removed, or identify tried to replace an established identity. Start a new browser session and get a new token; do not resend the old one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionReinitRequiredError"}}}},"429":{"description":"Too many requests from one client IP. Wait for Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitedError"}}},"headers":{"Retry-After":{"description":"Seconds until the collector admits requests again. Never replay the protected application action.","required":true,"schema":{"type":"string"}}}},"500":{"description":"The collector could not complete the request. Fail the protected action and do not retry automatically.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}},"503":{"description":"A temporary Site configuration or browser session failure; the code names what is unavailable. Only verify may retry (three attempts, 0-250 ms then 0-500 ms jitter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnavailableBody"}}}}}}},"/v1/discovery/openapi.json":{"get":{"operationId":"compatDiscoveredOpenApi","summary":"Compatibility API (/v1) discovered API export","description":"The reviewed actions of /api/v1/discovery/openapi.json in the v1 DiscoveredOpenApiDocument shape: exactly openapi 3.1.1, info {title: Discovered API, version: 1} and paths, with ETag/If-None-Match. 503 discovery_unavailable while discovery is off for the Site, discovery_not_ready while it is on but nothing has been observed yet. Failures are exactly {\"error\":\"<code>\"}.","parameters":[{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Customer API OpenAPI document","content":{"application/json":{"schema":{"type":"object","properties":{"openapi":{"const":"3.1.1"},"info":{"type":"object","properties":{"title":{"const":"Discovered API"},"version":{"const":"1"}},"required":["title","version"],"additionalProperties":false},"paths":{"type":"object"}},"required":["openapi","info","paths"],"additionalProperties":false}}},"headers":{"ETag":{"description":"Identifies this document revision.","required":true,"schema":{"type":"string"}}}},"304":{"description":"Document unchanged; no response body.","headers":{"ETag":{"description":"Identifies this document revision.","required":true,"schema":{"type":"string"}}}},"401":{"description":"The server key is missing, invalid or not sent with the Bearer scheme.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}}},"403":{"description":"The server key and session token belong to different Sites, or the call came from a browser page on the public collector origin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Unknown path or wrong method.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"429":{"description":"Too many requests from one client IP. Wait for Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitedError"}}},"headers":{"Retry-After":{"description":"Seconds until the collector admits requests again. Never replay the protected application action.","required":true,"schema":{"type":"string"}}}},"500":{"description":"The collector could not complete the request. Fail the protected action and do not retry automatically.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}},"503":{"description":"The discovered document is not ready or unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoveryUnavailableBody"}}}}}}},"/v1/sessions/{sessionId}":{"get":{"operationId":"compatGetSession","summary":"Compatibility API (/v1) session lookup","description":"The v1 SessionResponse, exactly: {sessionId, status, identity, verdict}: status active or ended (consent withdrawn reads ended), verdict the action verdict (unknown after withdrawal), no score. Server key only, this Site only. Another Site’s session, an unknown, erased or deleted one and one past retention are all 404 session_not_found. Other methods are 405 (plain text, Allow: GET).","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"},"description":"The session ID the browser SDK returns (getSessionId()). A reference, not a credential."}],"responses":{"200":{"description":"Session result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompatSession"}}},"headers":{"Cache-Control":{"description":"Never store the result.","required":true,"schema":{"type":"string","enum":["no-store"]}}}},"400":{"description":"The session ID is malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"}}}},"401":{"description":"The server key is missing, invalid or not sent with the Bearer scheme.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}}},"404":{"description":"The session is unknown, belongs to another Site, was erased or deleted, or is past its retention. Which one is never said.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionNotFoundError"}}}},"405":{"description":"Use GET (plain text; Allow: GET).","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Too many requests from one client IP. Wait for Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitedError"}}},"headers":{"Retry-After":{"description":"Seconds until the collector admits requests again. Never replay the protected application action.","required":true,"schema":{"type":"string"}}}},"500":{"description":"The collector could not complete the request. Fail the protected action and do not retry automatically.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}},"503":{"description":"A temporary Site configuration or browser session failure; the code names what is unavailable. Only verify may retry (three attempts, 0-250 ms then 0-500 ms jitter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnavailableBody"}}}}}}},"/api/v1/sessions/{sessionId}":{"get":{"operationId":"getSession","summary":"Look up a session later: verdict, evidence, identity and decisions","description":"For a backend that stored the session ID with its own record (an exam attempt, an order) and checks it afterwards. Server key only, this Site only: another Site’s session, an unknown, erased or deleted one and one past retention are the same 404. Available while the session is kept: the Site’s session retention from its last activity (default 8 days, 1 to 400; at most 8 days after consent withdrawal). A read: it never extends the session or changes its verdict.","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","pattern":"^session_[A-Za-z0-9_-]{8,64}$"},"description":"The session ID the browser SDK returns (getSessionId()). A reference, not a credential."}],"responses":{"200":{"description":"Session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLookup"}}},"headers":{"Cache-Control":{"description":"Never store the result.","required":true,"schema":{"type":"string","enum":["no-store"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify":{"post":{"operationId":"verifyProtectedAction","summary":"Verify evidence before a protected action","description":"Apply your own authenticated account authorization and the returned decision. Unknown is not human. Optionally forward the incoming request’s Web Bot Auth headers to receive signed attribution. The server adapter retries valid 503 service_unavailable, config_unavailable or session_unavailable responses, and once on a 408/429 whose Retry-After fits, at most three attempts within one five-second deadline; it never executes the protected application action.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"request":{"$ref":"#/components/schemas/ForwardedRequest"},"evidence":{"$ref":"#/components/schemas/EdgeEvidenceInput"},"purpose":{"type":"string","enum":["protect","display"],"default":"protect","description":"display marks a check that only shows status (a header badge). It is recorded but never alerts, queues for review or counts as protection."},"targetId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$","description":"Opaque ID of the object acted on, chosen by your backend (for example report_1042). Never an email, name or other request value."},"userId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"accountId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."}},"required":["sessionToken","actionId"],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","actionId":"export_report","targetId":"report_1042","userId":"usr_123","accountId":"acct_456"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the request contradicts what is already recorded; message says what","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"Observatory-Attribution","in":"header","required":false,"schema":{"type":"string","enum":["declared"]},"description":"declared: also return the declared (self-reported user agent) attribution tier (strength declared, kind self-declared, evidence type declared-user-agent). Without it the response uses only the Attribution values listed in this document; clients generated from it keep working."}]}},"/api/v1/identify":{"post":{"operationId":"identifySession","summary":"Associate server-verified identity","description":"Derive IDs from your authenticated session and account membership. Replacing established identity requires a fresh browser session. No automatic retries.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"userId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"accountId":{"type":["string","null"],"minLength":1,"maxLength":512,"pattern":"\\S","description":"Stable authenticated ID. Maximum 512 Unicode code points for server-verified identity (browser-supplied IDs stay at 128 UTF-16 code units); whitespace is trimmed."},"accountTraits":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Optional owner-supplied labels for accountId (requires accountId): a display name, a plan, your CRM account ID, a segment and the account owner's email domain (the domain only; an email address is refused), used for dashboard display, filters and exports only. Never sent by the browser SDK, never used for verdicts or authorization, removed when the account is erased. null clears a field.","properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":80},"plan":{"type":["string","null"],"minLength":1,"maxLength":40,"pattern":"^[\\p{L}\\p{N} ._+-]+$"},"crmId":{"type":["string","null"],"minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9._:/#-]+$"},"segment":{"type":["string","null"],"minLength":1,"maxLength":40,"pattern":"^[\\p{L}\\p{N} ._+-]+$"},"ownerDomain":{"type":["string","null"],"minLength":3,"maxLength":253,"description":"A domain such as acme.com, never an email address."}}},"userTraits":{"type":"object","additionalProperties":false,"minProperties":1,"maxProperties":1,"description":"Optional, requires userId: the user's email or email domain, so Settings can mark your team as known humans by domain (see Your agent report). Only the domain after @ is kept; the address is never stored. Never used for verdicts, authorization or billing; removed when the user is erased. null forgets it.","properties":{"email":{"type":["string","null"],"maxLength":320},"emailDomain":{"type":["string","null"],"maxLength":253}}}},"required":["sessionToken"],"anyOf":[{"required":["userId"],"properties":{"userId":{"type":"string"}}},{"required":["accountId"],"properties":{"accountId":{"type":"string"}}}],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","userId":"usr_123","accountId":"acct_456"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"const":true}},"required":["accepted"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the session already has a different server-verified identity; start a new browser session for the other user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/usage":{"post":{"operationId":"recordCompletedUsage","summary":"Report completed chargeable units","description":"Requires server-verified identity. Identical eventId/session/action/units reuse returns the original record with replayed=true, even after the browser session expired or was withdrawn (the eventId is checked before the token). New work needs a live session token, so report it within the 24-hour session life. Conflicting reuse returns 409 conflict. This records your attestation of completion; it does not independently verify work or charge money. No automatic retries.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"executionId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Optional stored execution ID. Must match this site, session, trusted account and action."},"units":{"type":"integer","minimum":1,"maximum":1000000}},"required":["sessionToken","eventId","actionId","units"],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","eventId":"event_00000001","actionId":"export_report","units":1}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeteredUsage"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the eventId was already used with different facts; do not retry, use a new eventId. identity_required: the session has no server-verified identity yet; call /v1/identify (or /api/v1/identify) for this session, then send the same request again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events":{"post":{"operationId":"recordCustomEvents","summary":"Record custom events from your server (shape-only: a name, a time, IDs and tags)","description":"Counted per day, per account and month; never a message, parameters or payload: unknown fields are refused. A resent eventId is a replay. See [Custom events, tags and debug mode](/docs/custom-events).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sdk":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name"]},"events":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"},"name":{"type":"string","pattern":"^[a-z][a-z0-9_.]{0,63}$"},"at":{"type":"integer"},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"channel":{"type":"string","enum":["server","mcp","api"]},"tags":{"type":"object","maxProperties":10,"propertyNames":{"pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Categories only: keys that suggest personal data or secrets, identifier-like values and free text are dropped and counted in tagsDropped."}},"required":["eventId","name","at"],"additionalProperties":false}}},"required":["events"],"additionalProperties":false,"example":{"events":[{"eventId":"event_00000001","name":"report_exported","at":1762300800000,"accountId":"acct_456","userId":"usr_123","tags":{"format":"csv"}}]}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer"},"replayed":{"type":"integer"},"tagsDropped":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"field":{"type":["string","null"]},"error":{"type":"string"}},"required":["index","field","error"]}}},"required":["accepted","replayed","tagsDropped","rejected"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: an eventId was already used with a different name or time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/mcp/calls":{"post":{"operationId":"recordMcpCalls","summary":"Record tool calls through your MCP server (Telltale for MCP servers)","description":"Shapes only: unknown fields are refused, never dropped, so no argument, result, error text or header can be stored. Each call is one approved-route call with channel mcp, once per eventId; a bad call is rejected alone. Sent by the MCP adapter (/adapters/v1/mcp.js; see [Telltale for MCP servers](/docs/mcp)). There is no field for arguments, results or error text.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sdk":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name"]},"server":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name"]},"dropped":{"type":"integer","minimum":0},"calls":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"},"tool":{"type":"string","pattern":"^[A-Za-z0-9_./-]{1,128}$"},"startedAt":{"type":"integer"},"endedAt":{"type":"integer"},"responseBytes":{"type":["integer","null"],"minimum":0,"maximum":1000000000000},"responseTokens":{"type":["integer","null"],"minimum":0,"maximum":1000000000000},"outcome":{"type":"string","enum":["ok","error","refused","cancelled"]},"errorClass":{"type":"string","enum":["tool_error","invalid_params","unknown_tool","internal_error","timeout","other"]},"refusedBy":{"type":"string","enum":["deny","hold","unreachable"]},"client":{"type":"object","properties":{"name":{"type":"string","maxLength":64},"version":{"type":"string","maxLength":32}},"required":["name"]},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"session":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-f0-9]{32,64}$"},"source":{"type":"string","enum":["customer","transport","derived"]}},"required":["key","source"]},"reason":{"type":"string","maxLength":280},"decisionId":{"type":"string"},"tokenId":{"type":"string"},"tags":{"type":"object","maxProperties":10,"propertyNames":{"pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Categories only: keys that suggest personal data or secrets, identifier-like values and free text are dropped and counted in tagsDropped."}},"required":["eventId","tool","startedAt","endedAt","outcome"],"additionalProperties":false}}},"required":["calls"],"additionalProperties":false,"example":{"server":{"name":"docs-server","version":"1.4.0"},"calls":[{"eventId":"call_00000001","tool":"search_docs","startedAt":1794268800000,"endedAt":1794268801200,"outcome":"ok","client":{"name":"claude-ai","version":"1.0"},"accountId":"acct_456"}]}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer"},"replayed":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"field":{"type":["string","null"]},"error":{"type":"string"}},"required":["index","field","error"]}}},"required":["accepted","replayed","rejected"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: an eventId was already used with different facts; the call is rejected alone in rejected[]","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/mcp/decide":{"post":{"operationId":"decideMcpCall","summary":"Check your rule (Control › Rules) for a protected MCP tool before it runs","description":"The AI agent branch of the tool's saved rule decides (every MCP caller is an agent's client); with no saved rule the defaults apply (AI agent: hold for review). A medium-confidence call that the rule would deny is held for review instead unless the site blocks medium confidence. A valid agent token issued with mcpTools turns a hold into allow; a held call approved in the dashboard or by POST /api/v1/mcp/decisions/{id}/step-up is allowed once within 15 minutes with approvedDecisionId. A hold is returned as decision \"review\" with a decisionId: the dashboard shows it as Held for review; poll GET /api/v1/mcp/decisions/{decisionId} or settle it with your own step-up. Leave client out and confidence is medium. Not authorization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"client":{"type":"object","properties":{"name":{"type":"string","maxLength":64},"version":{"type":"string","maxLength":32}},"required":["name"],"additionalProperties":false},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"agentToken":{"type":"string"},"approvedDecisionId":{"type":"string"}},"required":["tool"],"additionalProperties":false,"example":{"tool":"create_invoice","client":{"name":"claude-ai","version":"1.0"},"accountId":"acct_456","userId":"usr_123"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["allow","review","deny"]},"basis":{"type":"string","enum":["rule","default_rule","no_rule","agent_token","approval"]},"confidence":{"type":"string","enum":["high","medium"],"description":"From the declared client: high for a known AI app, medium for debugging tools (MCP Inspector) and unknown or missing clients."},"client":{"type":["object","null"]},"heldInsteadOfDeny":{"type":"boolean"},"decisionId":{"type":"string"},"tool":{"type":"string"},"accountId":{"type":["string","null"]},"userId":{"type":["string","null"]}},"required":["decision","basis"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: approvedDecisionId was already used, has expired, or belongs to another tool or account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/api-calls":{"post":{"operationId":"recordApiCalls","summary":"Record calls to your own API (Telltale for APIs)","description":"Shapes only: unknown fields are refused, never dropped, so no body, query value, header or credential can be stored. A call with agent evidence (agent token, Web Bot Auth signature, a declared name checked against its operator's IP list, an AI agent provider's published IP range, or a declared name) is one approved-route call with channel api, once per eventId; every other call is Unknown (never Human) and only counted per route and day. Sent by the API middleware (/adapters/v1/api.js; see [Telltale for APIs](/docs/api-channel)).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"batchId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$","description":"Kept across retries: a resent batch is a replay as a whole."},"sdk":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name"]},"framework":{"type":"string","enum":["node","express","next","hono","fastify","other"]},"dropped":{"type":"integer","minimum":0},"calls":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"object","properties":{"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"},"method":{"type":"string","enum":["GET","HEAD","POST","PUT","PATCH","DELETE","OPTIONS"]},"route":{"type":"string","pattern":"^/[A-Za-z0-9_.~:{}\\[\\]*+/-]{0,255}$","description":"The route template (/v1/invoices/:id), never a raw path or a query string. A segment that looks like a value (three or more digits, a UUID, a long hex run) is refused."},"status":{"type":"integer","minimum":100,"maximum":599},"startedAt":{"type":"integer"},"endedAt":{"type":"integer"},"responseBytes":{"type":["integer","null"],"minimum":0,"maximum":1000000000000},"responseTokens":{"type":["integer","null"],"minimum":0,"maximum":1000000000000},"size":{"type":"string","enum":["empty","under_1kb","1kb_10kb","10kb_100kb","100kb_1mb","over_1mb","unknown"]},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"sampleRate":{"type":"number","exclusiveMinimum":0,"maximum":1},"decisionId":{"type":"string"},"refusedBy":{"type":"string","enum":["deny","hold","unreachable"]},"evidence":{"type":"object","properties":{"ip":{"type":"string","description":"Used once for the provider and declared-name checks, never stored."},"secure":{"type":"boolean"},"signature":{"type":"object","properties":{"authority":{"type":"string"},"path":{"type":"string"},"headers":{"type":"object","properties":{"signature":{"type":"string"},"signature-input":{"type":"string"},"signature-agent":{"type":"string"}},"required":["signature","signature-input"],"additionalProperties":false}},"required":["authority","headers"],"additionalProperties":false,"description":"A Web Bot Auth signature's own headers, verified once and never stored."},"declared":{"type":"object","properties":{"id":{"type":"string"},"via":{"type":"string","enum":["user-agent","header"]}},"required":["id"],"additionalProperties":false},"agentToken":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"SHA-256 hex of \"agent-token:\" + the Telltale agent token; the token never leaves your server."}},"required":[],"additionalProperties":false,"description":"Transient agent-evidence material: never stored, logged or returned."},"tags":{"type":"object","maxProperties":10,"propertyNames":{"pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Categories only: keys that suggest personal data or secrets, identifier-like values and free text are dropped and counted in tagsDropped."}},"required":["eventId","method","route","status","startedAt","endedAt"],"additionalProperties":false}}},"required":["calls"],"additionalProperties":false,"example":{"framework":"express","calls":[{"eventId":"call_00000001","method":"GET","route":"/v1/invoices/:id","status":200,"startedAt":1794268800000,"endedAt":1794268800085,"size":"1kb_10kb","accountId":"acct_456"}]}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer"},"replayed":{"type":"integer"},"agentCalls":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"field":{"type":["string","null"]},"error":{"type":"string"}},"required":["index","field","error"]}}},"required":["accepted","replayed","agentCalls","rejected"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: batchId or an eventId was already used with different facts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/mcp/why":{"get":{"operationId":"getMcpWhyField","summary":"Whether the workspace keeps the why field (opt in)","description":"The Telltale MCP SDK reads this at start and every five minutes, and adds the optional reason parameter to the tools named in its why option only while enabled is true. Off by default; a workspace owner or admin turns on \"Understand what agents are trying to do\".","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxLength":{"type":"integer"}},"required":["enabled","maxLength"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent-feedback/state":{"get":{"operationId":"getAgentFeedback","summary":"Whether the workspace keeps agent feedback (opt in)","description":"The Telltale MCP SDK reads this at start and lists its feedback tool only while enabled is true. Off by default; a workspace owner or admin turns it on in Settings › Data & privacy.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxLength":{"type":"integer"}},"required":["enabled","maxLength"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent-feedback":{"post":{"operationId":"recordAgentFeedback","summary":"Record what an AI agent said it needed and could not find (agent feedback)","description":"Stored only while the workspace has agent feedback on; otherwise answered {stored: false, reason: \"off\"} and nothing is kept. Never feeds verdicts, rules, confidence or billing. Sent by adapters/mcp (feedback: true).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"},"text":{"type":"string","maxLength":2000,"description":"What the agent wrote. Redacted here (e-mail addresses, credential-like strings, long digit runs and URL query strings) and cut to 500 characters before it is stored."},"tool":{"type":"string","maxLength":64},"at":{"type":"integer"},"client":{"type":"object","properties":{"name":{"type":"string","maxLength":64},"version":{"type":"string","maxLength":32}},"required":["name"]},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"session":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-f0-9]{32,64}$"},"source":{"type":"string","enum":["customer","transport","derived"]}},"required":["key","source"]}},"required":["eventId","text"],"additionalProperties":false,"example":{"eventId":"feedback_00000001","text":"Needed a tool to export last month’s invoices as CSV; found only list_invoices.","tool":"list_invoices","at":1762300800000,"client":{"name":"Claude Code","version":"2.1.0"},"accountId":"acct_456","userId":"usr_123"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"stored":{"type":"boolean"},"replayed":{"type":"boolean"},"id":{"type":"string"},"reason":{"type":"string","enum":["off"]}},"required":["stored"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the request contradicts what is already recorded; message says what","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/deep-insights":{"get":{"operationId":"getDeepInsights","summary":"Whether the Site stores Deep insights captures (opt in)","description":"The Telltale MCP and API SDKs read this at start and send captured fields only while enabled is true. Off by default; the Site owner turns it on in Settings › Deep insights. Credential-like fields and values are never stored.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"retentionDays":{"type":"integer","enum":[7,14,30]},"limits":{"type":"object","properties":{"fields":{"type":"integer"},"valueChars":{"type":"integer"},"arrayItems":{"type":"integer"},"callBytes":{"type":"integer"},"pathChars":{"type":"integer"}},"required":["fields","valueChars","arrayItems","callBytes","pathChars"]}},"required":["enabled","retentionDays","limits"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/api-calls/decide":{"post":{"operationId":"decideApiCall","summary":"Check your rule (Control › Rules) for a protected API route before the handler runs","description":"The branch of the call's verdict decides: AI agent (any agent evidence) or Unknown; there is no Human branch on this channel. With no saved rule the defaults apply (AI agent: hold for review; Unknown: allow). A Declared name is medium confidence: a Deny becomes a hold unless the site blocks medium confidence. A valid agent token issued with apiRoutes for this route turns a hold into allow; a held call approved in the dashboard or by the step-up endpoint is allowed once within 15 minutes with approvedDecisionId. A hold is returned as decision \"review\" with a decisionId and a stepUp hint: the dashboard shows it as Held for review; poll GET /api/v1/api-calls/decisions/{decisionId} or settle it with your own step-up. Not authorization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"type":"string","enum":["GET","HEAD","POST","PUT","PATCH","DELETE","OPTIONS"]},"route":{"type":"string"},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"accountId":{"type":"string","maxLength":512},"userId":{"type":"string","maxLength":512},"evidence":{"type":"object","description":"The same transient evidence material as a recorded call."},"approvedDecisionId":{"type":"string"}},"required":["method","route"],"additionalProperties":false,"example":{"method":"POST","route":"/v1/invoices","actionId":"create_invoice","accountId":"acct_456","userId":"usr_123"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["allow","review","deny"]},"basis":{"type":"string","enum":["rule","default_rule","agent_token","approval"]},"verdict":{"type":"string","enum":["agent","unknown"]},"agent":{"anyOf":[{"type":"object","properties":{"name":{"type":["string","null"]},"howWeKnow":{"type":"string","enum":["Verified by token","Verified","Published IP range","Declared"]},"confidence":{"type":"string","enum":["high","medium"]}},"required":["name","howWeKnow","confidence"]},{"type":"null"}]},"actionId":{"type":"string"},"route":{"type":"string"},"decisionId":{"type":"string"},"heldInsteadOfDeny":{"const":true},"stepUp":{"$ref":"#/components/schemas/StepUpHint"},"accountId":{"type":["string","null"]},"userId":{"type":["string","null"]}},"required":["decision","basis","verdict","actionId"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: approvedDecisionId was already used, has expired, or belongs to another route or account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/api-calls/decisions/{decisionId}":{"get":{"operationId":"getApiDecision","summary":"The state of a held API call","description":"Poll this while a call is held (decision review). review.status is pending while waiting, then approved (the next decide with approvedDecisionId is allowed once, within 15 minutes; redeemed turns true when it is used) or rejected. decision stays the original answer (review); approval shows in review, not in decision. An unknown, expired or other Site’s decisionId is 404.","parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDecisionState"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/api-calls/decisions/{decisionId}/step-up":{"post":{"operationId":"stepUpApiCall","summary":"Report your own step-up for a held API call","description":"passed approves the held call for one use within 15 minutes (the next decide with approvedDecisionId); failed rejects it. Evidence your check succeeded, not authorization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"string","enum":["passed","failed"]},"method":{"type":"string","enum":["passkey","mfa","turnstile","own_approval","other"]}},"required":["result"],"additionalProperties":false,"example":{"result":"passed","method":"passkey"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"decisionId":{"type":"string"},"status":{"type":"string","enum":["approved","rejected"]},"expiresAt":{"type":["integer","null"]}},"required":["decisionId","status","expiresAt"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the decision is not held, was already settled or has expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}]}},"/api/v1/mcp/decisions/{decisionId}":{"get":{"operationId":"getMcpDecision","summary":"The state of a held MCP tool call","description":"Poll this while a call is held (decision review). review.status is pending while waiting, then approved (the next decide with approvedDecisionId is allowed once, within 15 minutes; redeemed turns true when it is used) or rejected. decision stays the original answer (review); approval shows in review, not in decision. An unknown, expired or other Site’s decisionId is 404.","parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDecisionState"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/mcp/decisions/{decisionId}/step-up":{"post":{"operationId":"stepUpMcpCall","summary":"Report your own step-up for a held MCP tool call","description":"passed approves the held call for one use within 15 minutes (the next decide with approvedDecisionId); failed rejects it. Evidence your check succeeded, not authorization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelStepUpRequest","example":{"result":"passed","method":"passkey"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelStepUpResult"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the decision is not held, was already settled or has expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}]}},"/api/v1/agent-tokens":{"post":{"operationId":"issueAgentToken","summary":"Issue a scoped, short-lived agent token for one account","description":"409 agent_tokens_disabled while agent tokens are off for the Site (Settings); 409 tool_not_approved or tool_not_read_only for a tool that is not reviewed and enabled, or that changes data (writes stay in the page). Retrying issues another token: keep the first answer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","minLength":1,"maxLength":512},"userId":{"type":["string","null"],"minLength":1,"maxLength":512},"tools":{"type":"array","maxItems":20,"items":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"description":"Reviewed, enabled, read-only tools of this Site."},"mcpTools":{"type":"array","maxItems":20,"items":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"description":"Tools of your own MCP server this token may call."},"apiRoutes":{"type":"array","maxItems":20,"items":{"type":"string"},"description":"Routes of your own API, as \"METHOD /route/template\"."},"ttlSeconds":{"type":"integer","minimum":60,"maximum":86400,"default":900}},"required":["accountId"],"additionalProperties":true,"example":{"accountId":"acct_456","userId":"usr_123","mcpTools":["search_docs"],"ttlSeconds":900}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","pattern":"^tt_agent_","description":"Returned once; hand it to the agent. Never log it."},"tokenId":{"type":"string"},"accountId":{"type":"string"},"userId":{"type":["string","null"]},"tools":{"type":"array","items":{"type":"string"}},"mcpTools":{"type":"array","items":{"type":"string"}},"apiRoutes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":"number","description":"Unix epoch milliseconds."}},"required":["token","tokenId","accountId","userId","tools","expiresAt"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"agent_tokens_disabled: agent tokens are off for this Site (Settings). tool_not_approved: a tool in tools is not reviewed and enabled. tool_not_read_only: a tool in tools changes data (writes stay in the page). message names the tool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent-tokens/verify":{"post":{"operationId":"verifyAgentToken","summary":"Verify an agent token for one tool call","description":"A refusal is 200 {valid:false, reason} in fixed words, never an error; nothing is recorded for it. reason is unknown (no such token), expired, revoked, out_of_scope (the tool is not in the token), approval_changed (the tool was changed or disabled after the token was issued) or disabled (agent tokens are off for this Site).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"tool":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"eventId":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$","description":"Optional: a retry with the same eventId is the same call (replayed true), never a second metered call."}},"required":["token","tool"],"additionalProperties":true,"example":{"token":"tt_agent_PASTE_AGENT_TOKEN","tool":"search_docs","eventId":"call_00000001"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"valid":{"const":true},"tokenId":{"type":"string"},"accountId":{"type":"string"},"userId":{"type":["string","null"]},"tool":{"type":"string"},"expiresAt":{"type":"number","description":"Unix epoch milliseconds."},"callId":{"type":"string"},"replayed":{"type":"boolean"}},"required":["valid","tokenId","accountId","userId","tool","expiresAt","callId","replayed"]},{"type":"object","properties":{"valid":{"const":false},"reason":{"type":"string","enum":["unknown","expired","revoked","out_of_scope","approval_changed","disabled"]}},"required":["valid","reason"]}]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: eventId was already used for another token or tool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent-tokens/revoke":{"post":{"operationId":"revokeAgentTokens","summary":"Revoke one agent token, or every live token of an account","description":"Send exactly one of token (the token itself), tokenId (from the issue response) or accountId (every live token of that account). An empty body, or two of them, is 400 bad_request: nothing is ever revoked by default. Idempotent: revoking a revoked token again answers revoked 0.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Exactly one of token, tokenId or accountId.","properties":{"token":{"type":"string"},"tokenId":{"type":"string"},"accountId":{"type":"string","maxLength":512}},"minProperties":1,"maxProperties":1,"example":{"tokenId":"atok_PASTE_TOKEN_ID"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"integer","minimum":0,"description":"How many live tokens this call revoked."}},"required":["revoked"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the request contradicts what is already recorded; message says what","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workflow-trials":{"post":{"operationId":"recordWorkflowTrial","summary":"Record one controlled UI or tool task attempt","description":"Requires a live session with server-associated identity. The authenticated application backend attests matching task and test conditions; no independent success or time-savings guarantee. UI and tool runs share pairId, taskId, protocolId, scenarioId and appRevision. An identical eventId retry is idempotent while the record and session remain available; conflicting facts or a duplicate mode are 409. Keep opaque non-personal IDs, never inputs or outputs. Stored for 90 days with a 10,000-attempt per-Site limit and erased with its subject. See [Measured workflow comparisons](/docs/workflow-comparisons).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"eventId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"pairId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"taskId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"protocolId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"scenarioId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"appRevision":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$"},"controlled":{"const":true},"mode":{"type":"string","enum":["ui","tool"]},"outcome":{"type":"string","enum":["succeeded","failed","cancelled"]},"durationMs":{"type":"integer","minimum":1,"maximum":86400000},"retries":{"type":"integer","minimum":0,"maximum":10000}},"required":["sessionToken","eventId","pairId","taskId","protocolId","scenarioId","appRevision","controlled","mode","outcome","durationMs","retries"],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","eventId":"trial_export_ui_01","pairId":"pair_export_01","taskId":"task_export_report","protocolId":"protocol_2026_11","scenarioId":"scenario_monthly_export","appRevision":"v2.3.1","controlled":true,"mode":"ui","outcome":"succeeded","durationMs":42000,"retries":0}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the eventId was used with different facts, or this pair already has a run in this mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/discovery/openapi.json":{"get":{"operationId":"getDiscoveredOpenApi","summary":"Export the Site’s discovered customer API","parameters":[{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Customer API OpenAPI document","content":{"application/json":{"schema":{"type":"object","required":["openapi","info","paths"],"properties":{"openapi":{"type":"string"},"info":{"type":"object"},"paths":{"type":"object"}}}}},"headers":{"ETag":{"schema":{"type":"string"},"required":true,"description":"Current document entity tag."}}},"304":{"description":"Document unchanged; no response body.","headers":{"ETag":{"schema":{"type":"string"},"required":true,"description":"Current document entity tag."}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/usage/ledger":{"get":{"operationId":"getUsageLedger","summary":"Read the agent-usage ledger","parameters":[{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["days","accounts","entries"],"default":"days"},"description":"days: per UTC day, account and user; accounts: totals per account; entries: the entries with their evidence, oldest first."},{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Period start: Unix epoch ms or ISO 8601 date. Defaults to the last 30 whole UTC days. Clamped to 400 days for counts and to the Site usage retention for entries."},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"Period end (exclusive)."},{"name":"accountId","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"userId","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"usageId","in":"query","required":false,"schema":{"type":"string","pattern":"^meter_[a-f0-9]{64}$"},"description":"entries view: one unit."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"page.nextCursor from the previous response; it keeps that view, period and filters."}],"description":"Completed-work units (POST /api/v1/usage) counted as chargeable agent (high confidence: verified identity, an automation trace, injected input, sustained behaviour, a behaviour change or two or more behaviour families agreeing), behaviour-only agent (medium confidence, never charged), human or unknown, per Site, account, user and UTC day. Append-only: later changes are adjustment entries; erasure removes identity, never counts. Each entry carries its evidence (session IDs, verdict basis, how we know) and an idempotencyKey. No charges are created.","responses":{"200":{"description":"Agent-usage ledger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageLedger"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent-usage.json":{"get":{"operationId":"getAgentUsage","summary":"Report metered agent usage per account and category","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Period start: Unix epoch ms or ISO 8601 date. Defaults to the last 7 days: 7 whole UTC days ending today; clamped to the Site usage retention (90 days by default, up to 400 in Settings › Data)."},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"Period end; defaults to now and never passes it."},{"name":"accountId","in":"query","required":false,"schema":{"type":"string","maxLength":512}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"description":"Account rows per page."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"page.nextCursor from the previous response; it keeps that response period and account."},{"name":"series","in":"query","required":false,"schema":{"type":"string","enum":["day","week"]},"description":"Adds series: UTC-day or ISO-week buckets of the period that add up to totals."},{"name":"compare","in":"query","required":false,"schema":{"type":"string","enum":["previous"]},"description":"Adds previous (the period of the same length before since) and change."}],"description":"Retained tool executions and agent session-minutes for the requested period (default seven days, at most the Site usage retention). Totals and categories cover the whole period; accounts are paginated. Use for billing reconciliation; signed agent_usage.recorded webhooks carry the same units as they are ingested. No charges are created.","responses":{"200":{"description":"Agent usage report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUsage"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/activity.json":{"get":{"operationId":"getActivity","summary":"Human, agent and unknown interaction minutes and sessions per day","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Period start: Unix epoch ms or ISO 8601 date. Defaults to the last 7 days: 7 whole UTC days ending today; clamped to the Site usage retention (90 days by default, up to 400 in Settings › Data)."},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"Period end (exclusive); defaults to now."},{"name":"series","in":"query","required":false,"schema":{"type":"string","enum":["day","week"],"default":"day"}}],"description":"For BI tools: interaction minutes per class (human, agent, unknown, mixed) and daily session totals by last-observed verdict, in UTC-day or ISO-week buckets that partition [since, until). Compute a share as one class divided by total; it is a share of minutes or sessions, not a probability. Unknown is never counted as human.","responses":{"200":{"description":"Activity series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Activity"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/decisions/{decisionId}":{"get":{"operationId":"getDecision","summary":"Read a decision and its review state","parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}],"description":"approvalToken is returned only while an approved hold can still be redeemed. It is derived, not stored.","responses":{"200":{"description":"Decision","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Decision"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/decisions/{decisionId}/outcome":{"post":{"operationId":"reportDecisionOutcome","summary":"Report what your backend did (informational)","description":"Informational only: it never changes the decision. The first outcome wins; a different second outcome is 409 conflict.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","enum":["performed","blocked","failed"]},"reason":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"required":["outcome"],"example":{"outcome":"performed"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"decisionId":{"type":"string"},"outcome":{"type":"string"},"reason":{"type":["string","null"]},"at":{"type":"number","description":"Unix epoch milliseconds."},"informational":{"const":true}},"required":["decisionId","outcome","reason","at","informational"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: a different outcome was already reported for this decision","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}]}},"/api/v1/decisions/{decisionId}/review":{"post":{"operationId":"requestDecisionReview","summary":"Send a decision to the hosted review queue","description":"Use when your own policy holds an action that Telltale allowed. It only tightens: a denied decision cannot be reviewed (409). The body is an empty object {}.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","example":{}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Decision"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflict: the decision was denied, or is already in review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}]}},"/api/v1/decisions/{decisionId}/step-up":{"post":{"operationId":"reportStepUp","summary":"Report the result of your own step-up check for a held decision","description":"For a decision held for review (review), your backend may run its own check (passkey or MFA re-auth, Turnstile, its own approval) instead of waiting for the hosted queue. Checked like an approval: same Site, session, action and target, still waiting, within 15 minutes. passed approves and uses the hold at once (proceed true, one time; the same report within 60 s is answered again with replayed true); failed rejects it. Either way review.resolved is sent with via step-up. A pass is evidence, not authorization. Refusals are 409 step_up_not_held, step_up_mismatch, step_up_settled or step_up_expired.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"targetId":{"type":["string","null"],"pattern":"^[A-Za-z0-9_-]{1,128}$"},"result":{"type":"string","enum":["passed","failed"]},"method":{"type":"string","enum":["passkey","mfa","turnstile","own_approval","other"],"default":"other"}},"required":["sessionToken","actionId","result"],"example":{"sessionToken":"PASTE_BROWSER_SESSION_TOKEN","actionId":"export_report","targetId":"report_1042","result":"passed","method":"passkey"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResult"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"step_up_not_held (the decision is not waiting for review), step_up_mismatch (another session, action or target), step_up_settled (already approved or rejected) or step_up_expired (more than 15 minutes after the hold)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","pattern":"^decision_[A-Za-z0-9_-]{8,64}$"}}]}},"/api/v1/approvals/redeem":{"post":{"operationId":"redeemApproval","summary":"Redeem a one-time approval token","description":"Bound to the same session, action and target; valid once, for 15 minutes after approval. An approval is evidence, not authorization: run your own authorization before performing the write. Refusals are 409 approval_invalid, approval_used, approval_expired or approval_mismatch.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"approvalToken":{"type":"string","pattern":"^ao_ap_"},"sessionToken":{"type":"string","minLength":1,"maxLength":250,"description":"Opaque browser session token; do not log."},"actionId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"targetId":{"type":["string","null"],"pattern":"^[A-Za-z0-9_-]{1,128}$"}},"required":["approvalToken","sessionToken","actionId"],"example":{"approvalToken":"ao_ap_PASTE_APPROVAL_TOKEN","sessionToken":"PASTE_BROWSER_SESSION_TOKEN","actionId":"export_report","targetId":"report_1042"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Redemption"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme, or a malformed or unsigned session token) or session_reinit_required (a token this collector issued whose session expired, was revoked or was removed); use the code to distinguish","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the server key and session token belong to different Sites, or the call came from a browser page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: unknown path, or an ID in the path that is not on this Site","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"approval_invalid (not a token for this Site), approval_used (already redeemed), approval_expired (more than 15 minutes after approval) or approval_mismatch (another session, action or target)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"payload_too_large: the request body exceeds 131072 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integration-status":{"get":{"operationId":"getIntegrationStatus","summary":"Integration health for this Site","description":"Server key last use, verify outcomes by code over seven days (allow, review and deny from the decision history; failure codes such as unauthorized, session_reinit_required or rate_limited), webhook delivery status with a fixed reason category, live browser sessions, database health and warnings such as policy_missing. Holds no identities or request values.","responses":{"200":{"description":"Integration status","content":{"application/json":{"schema":{"type":"object","properties":{"siteId":{"type":"string"},"generatedAt":{"type":"integer"},"windowDays":{"type":"integer"},"behaviourCallsAgent":{"type":"boolean","description":"The Site setting \"Behaviour signals can call Agent\" (default true). True: behaviour clues of two or more different kinds agreeing in one page, with no physical-device input, make the session AI agent (high confidence). False: the same combination only keeps the session from reading Human (Unknown at most). Changed by a workspace owner or admin in Control › Rules; read-only here."},"database":{"type":"string","enum":["ok","unavailable"]},"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["current","retiring"]},"expires":{"type":["integer","null"]},"lastUsedAt":{"type":["integer","null"]}},"required":["id","status","expires","lastUsedAt"]}},"verify":{"type":"object","properties":{"total":{"type":"integer"},"byCode":{"type":"object","additionalProperties":{"type":"integer"}},"lastDecisionAt":{"type":["integer","null"]},"unsavedPolicyActions":{"type":"array","items":{"type":"object","properties":{"actionId":{"type":"string"},"count":{"type":"integer"},"lastAt":{"type":["integer","null"]}},"required":["actionId","count","lastAt"]}}},"required":["total","byCode","lastDecisionAt","unsavedPolicyActions"]},"webhooks":{"type":"array","items":{"type":"object"}},"sdk":{"type":"object","properties":{"liveSessions":{"type":["integer","null"]},"sessionCap":{"type":["integer","null"]},"lastEvidenceAt":{"type":["integer","null"]}},"required":["liveSessions","sessionCap","lastEvidenceAt"]},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}},"required":["siteId","generatedAt","windowDays","behaviourCallsAgent","database","keys","verify","webhooks","sdk","warnings"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/decisions.json":{"get":{"operationId":"listDecisions","summary":"List decisions for this Site, newest first, one page at a time","description":"Ordered by created time, newest first (ties by decision ID). Follow page.nextCursor until it is null to read every decision in the period; page.total counts all matches. from and to take Unix epoch ms or an ISO 8601 date or date-time, like the other exports.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":500},"description":"Decisions per page (1–1000, default 500)."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"page.nextCursor from the previous page. Opaque."},{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Period start (inclusive): Unix epoch ms or ISO 8601 date or date-time."},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"Period end (exclusive): Unix epoch ms or ISO 8601 date or date-time."},{"name":"actionId","in":"query","required":false,"schema":{"type":"string"}},{"name":"decision","in":"query","required":false,"schema":{"type":"string"}},{"name":"verdict","in":"query","required":false,"schema":{"type":"string"}},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"}},{"name":"userId","in":"query","required":false,"schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"schema":{"type":"string"}},{"name":"purpose","in":"query","required":false,"schema":{"type":"string"}},{"name":"review","in":"query","required":false,"schema":{"type":"string","enum":["waiting","approved","rejected","expired","cancelled"]},"description":"What became of a held decision: waiting, approved, rejected (closed as expired left out), expired (closed as expired) or cancelled."},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":100},"description":"Search (case-insensitive): part of the account ID or account name, user ID, action ID or name, agent name or session ID."}],"responses":{"200":{"description":"Decisions","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"const":"1"},"siteId":{"type":"string"},"decisions":{"type":"array","items":{"type":"object"}},"page":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":1000},"total":{"type":"integer","minimum":0},"nextCursor":{"type":["string","null"]}},"required":["limit","total","nextCursor"]}},"required":["version","siteId","decisions","page"]}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/discovery/workflows.json":{"get":{"operationId":"getWorkflowDefinitions","summary":"Export saved workflow definitions","responses":{"200":{"description":"Workflow definitions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowExport"}}}},"400":{"description":"bad_request: the request is invalid; message says what to fix and field names the request field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"unauthorized (missing or invalid server key, missing Bearer scheme)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"forbidden: the call came from a browser page or another Site; call from your server with this Site’s server key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"not_found: nothing with this ID on this Site (or it is past retention), or an unknown path","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited: wait for Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until retry admission; application actions must not be replayed.","required":true,"schema":{"type":"string"}}}},"500":{"description":"internal_error: fail the protected action; do not retry automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"service_unavailable: temporarily unavailable; only verify retries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}