{"openapi":"3.1.0","info":{"title":"camada-backend","version":"0.2.3","description":"camada app API (contracts §B). Same-origin session cookie; every mutation is audited; errors are `{ error: { code, message } }`; request bodies over 256 KB are refused with 413 `payload_too_large` (1 MB for the Stripe webhook); lists are `{ items, next_cursor }`; times are epoch ms; /v1 allows 600 requests/min per user and 120 failed-session requests/min per IP, per Cloudflare location across isolates, and over that is 429 `rate_limited` with Retry-After."},"servers":[{"url":"/api"}],"security":[{"session":[]}],"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"snake_case machine code"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"User":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"image":{"oneOf":[{"type":"string","description":"https profile picture URL from the sign-in provider (Google), or null"},{"type":"null"}]}},"required":["id","email","name","image"]},"Org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"created":{"type":"integer","description":"epoch milliseconds"},"chat_retention_days":{"type":"integer","minimum":7,"maximum":365,"description":"MEM-06: how long chat transcripts live"},"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["id","name","slug","created","chat_retention_days","role"]},"Member":{"type":"object","properties":{"id":{"type":"string","description":"same as user_id"},"user_id":{"type":"string"},"email":{"type":"string"},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"role":{"type":"string","enum":["admin","member","viewer"]},"created":{"type":"integer","description":"epoch milliseconds"}},"required":["id","user_id","email","name","role","created"]},"Invitation":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","member","viewer"]},"state":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["id","email","role","state","expires_at"]},"Project":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"slug":{"type":"string","pattern":"^[a-z0-9-]{2,32}$"},"name":{"type":"string"},"env":{"type":"string","enum":["production","staging","development"]},"learning_days":{"type":"integer"},"learning_started":{"type":"integer","description":"epoch milliseconds"},"created":{"type":"integer","description":"epoch milliseconds"}},"required":["id","org_id","slug","name","env","learning_days","learning_started","created"]},"KeyInfo":{"type":"object","properties":{"fp":{"type":"string"},"created":{"type":"integer","description":"epoch milliseconds"},"last_ingest_used":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"last_snapshot_used":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["fp","created","last_ingest_used","last_snapshot_used"]},"Keys":{"type":"object","properties":{"current":{"$ref":"#/components/schemas/KeyInfo"},"previous":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/KeyInfo"},{"type":"object","properties":{"expires":{"type":"integer","description":"epoch milliseconds"}},"required":["expires"]}]},{"type":"null"}]},"revoked":{"oneOf":[{"type":"object","properties":{"fp":{"type":"string"},"revoked":{"type":"integer","description":"epoch milliseconds"},"last_ingest_attempt":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"last_snapshot_attempt":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["fp","revoked","last_ingest_attempt","last_snapshot_attempt"]},{"type":"null"}]}},"required":["current","previous"]},"Run":{"type":"object","properties":{"id":{"type":"string"},"started":{"type":"integer","description":"epoch milliseconds"},"finished":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"ok":{"oneOf":[{"type":"boolean","description":"true: the run finished; false: it failed, or recorded no outcome within 30 minutes of its start; null: still running"},{"type":"null"}]},"events":{"oneOf":[{"type":"integer"},{"type":"null"}]},"entities":{"oneOf":[{"type":"integer","description":"entities seen this run"},{"type":"null"}]},"grey":{"oneOf":[{"type":"integer","description":"grey-zone entities this run, including ones already handled"},{"type":"null"}]},"verdicts":{"oneOf":[{"type":"integer","description":"verdicts this run wrote; equals the count in the run report; null until the run finishes. entities, grey, verdicts and investigated are on the runs list and a single run only, absent from Health.last_run and entity detail runs[]"},{"type":"null"}]},"investigated":{"oneOf":[{"type":"integer","description":"verdicts that got a deep investigation with tools; a subset of verdicts"},{"type":"null"}]},"blocked":{"oneOf":[{"type":"integer"},{"type":"null"}]},"error":{"oneOf":[{"type":"string"},{"type":"null"}],"description":"why the run failed (up to 500 characters), or \"no outcome recorded within 30 minutes of the start\" for a run that never settled; null while running and after success. Present on the runs list and a single run; absent from Health.last_run and entity detail runs[]"}},"required":["id","started","finished","ok","events","blocked"]},"Health":{"type":"object","properties":{"last_ingest":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"last_run":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/Run"},{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}]},{"type":"null"}],"description":"the newest run with an outcome (finished, or none 30 minutes after it started: ok false, finished null); a run in progress is not reported, so this is null until the first run settles. ok is never null here"},"snapshot":{"oneOf":[{"type":"object","properties":{"version":{"type":"integer"},"expires":{"type":"integer","description":"epoch milliseconds"},"rows":{"type":"integer"}},"required":["version","expires","rows"]},{"type":"null"}]},"last_poll":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["last_ingest","last_run","snapshot","last_poll"]},"TenantInfo":{"type":"object","properties":{"config":{"type":"object","description":"edge-analyst tenant config without secrets; below admin: customer settings only","additionalProperties":true},"learning_until":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"learning_ended":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"policy":{"type":"string","enum":["recommend","auto"]},"min_confidence":{"oneOf":[{"type":"number"},{"type":"null"}]},"effective_policy":{"type":"string","enum":["recommend","auto"]},"keys":{"$ref":"#/components/schemas/Keys","description":"admins only; omitted below admin"},"key_rotation_pending":{"type":"boolean","description":"below admin only (in place of keys): true iff a previous key is still live; absent for admins, whose keys.previous says it"},"health":{"$ref":"#/components/schemas/Health"}},"required":["config","learning_until","policy","health"]},"SdkVersion":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"latest":{"oneOf":[{"type":"string","description":"null for a package camada does not publish"},{"type":"null"}]},"outdated":{"type":"boolean"}},"required":["name","version","latest","outdated"]},"Freshness":{"type":"object","properties":{"last_event_ts":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"beacon_seen":{"type":"boolean"},"app_context_seen":{"type":"boolean"},"proxy_suspected":{"type":"boolean"},"bad_ip_suspected":{"type":"boolean","description":"Optional (false on older edges): set by any SDK-resolved malformed-ip row in an analysis run (a Logpush ClientIP never sets it) and cleared by a run that has events or beacons and no such row; such events are stored ip-less and cannot be blocked by IP."},"taps":{"type":"array","items":{"type":"string"}},"sdk_versions":{"type":"array","items":{"$ref":"#/components/schemas/SdkVersion"},"description":"D36: what each tap reported, with the latest version beside it"},"sdk_versions_raw":{"type":"array","items":{"type":"string","description":"the `@camada/node/0.1.0` strings edge-analyst sent"}},"snapshot_v4_seen":{"type":"boolean"},"snapshot_v5_seen":{"type":"boolean","description":"an SDK has polled a v5 snapshot, so custom rules are enforced per request (§D3)"},"node_tlsx_seen":{"type":"boolean","description":"Optional (false on older analysts): the tenant's sdk-node rows have carried a trusted TLS fingerprint, so the analyst stamps TLS_FP on them and TLS-fingerprint rules are observable and enforceable for a node tenant (camada-all-ga9a)"}},"required":["last_event_ts","beacon_seen","app_context_seen","proxy_suspected","taps","sdk_versions"]},"Learning":{"type":"object","properties":{"active":{"type":"boolean"},"until":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"policy":{"type":"string","enum":["recommend","auto"]},"effective_policy":{"type":"string","enum":["recommend","auto"]},"min_confidence":{"oneOf":[{"type":"number"},{"type":"null"}]},"learning_ended":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"day":{"type":"integer"},"of":{"type":"integer"}},"required":["active","until","policy","effective_policy","min_confidence","learning_ended","day","of"]},"SetupStep":{"type":"object","properties":{"id":{"type":"string","enum":["tenant","sdk","first_event","beacon","proxy","app_context","enforcement"]},"state":{"type":"string","enum":["todo","done","warn"]},"detail":{"type":"string"}},"required":["id","state","detail"]},"Action":{"type":"object","properties":{"id":{"type":"string"},"severity":{"type":"string","enum":["info","warn","error"]},"title":{"type":"string"},"detail":{"type":"string"},"href":{"type":"string"}},"required":["id","severity","title","detail","href"]},"EntityRow":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","session","tlsx","asn","path","cfp"],"description":"what the key is; cfp is a client fingerprint (read-only: never allow-listed or blocked); a path finding is a one-try-per-IP farm alert (never blocked; key is a path or '*')"},"key":{"type":"string"},"latest":{"$ref":"#/components/schemas/Finding"},"verdict_count":{"type":"integer"},"session_count":{"type":"integer","description":"distinct flagged sessions filed under this IP"},"first_ts":{"type":"integer","description":"epoch milliseconds"},"last_ts":{"type":"integer","description":"epoch milliseconds"},"max_confidence":{"type":"number"},"worst_verdict":{"type":"string","enum":["malicious","suspicious","benign"]},"score":{"oneOf":[{"type":"integer"},{"type":"null"}]}},"required":["key_type","key","latest","verdict_count","session_count","first_ts","last_ts","max_confidence","worst_verdict","score"]},"EntityRequest":{"type":"object","properties":{"ts":{"type":"integer","description":"epoch milliseconds"},"dur":{"oneOf":[{"type":"integer","description":"response latency in ms; null unless the tap is an SDK that reports it"},{"type":"null"}]},"m":{"oneOf":[{"type":"string"},{"type":"null"}]},"p":{"oneOf":[{"type":"string"},{"type":"null"}]},"q":{"type":"string"},"st":{"oneOf":[{"type":"integer"},{"type":"null"}]},"sid":{"oneOf":[{"type":"string"},{"type":"null"}]},"rt":{"oneOf":[{"type":"string","description":"route template"},{"type":"null"}]},"et":{"oneOf":[{"type":"string","description":"app track() event"},{"type":"null"}]},"blk":{"oneOf":[{"type":"string","description":"why the SDK did not serve the request as-is"},{"type":"null"}]},"rule":{"oneOf":[{"type":"string","description":"custom rule id that blocked it"},{"type":"null"}]},"warn":{"oneOf":[{"type":"string","description":"custom rule id that warned"},{"type":"null"}]},"challenged":{"type":"boolean","description":"the visitor passed the SDK challenge"},"outcome":{"oneOf":[{"type":"string","enum":["blocked","challenged","challenge_api","warned"],"description":"how the SDK answered: 'challenged' = a browser challenge page was served; 'challenge_api' = an API call under a challenge, passed with an x-camada-challenge: 1 signal instead of a page (an older SDK's 403 refusal counts here too)"},{"type":"null"}]},"tap":{"oneOf":[{"type":"string"},{"type":"null"}]},"ua_id":{"oneOf":[{"type":"integer","description":"index into the response uas[]"},{"type":"null"}]},"ip":{"oneOf":[{"type":"string","description":"the requesting address; present on a cidr (/64) timeline only"},{"type":"null"}]}},"required":["ts","dur","m","p","q","st","sid","rt","et","blk","rule","warn","challenged","outcome","tap","ua_id"]},"Finding":{"type":"object","properties":{"id":{"type":"string"},"ts":{"type":"integer","description":"epoch milliseconds"},"key_type":{"type":"string","enum":["ip","cidr","session","tlsx","asn","path","cfp"],"description":"what the key is; cfp is a client fingerprint (read-only: never allow-listed or blocked); a path finding is a one-try-per-IP farm alert (never blocked; key is a path or '*')"},"key":{"type":"string"},"tap":{"type":"string"},"verdict":{"type":"string"},"category":{"type":"string"},"confidence":{"type":"number"},"action":{"type":"string","enum":["blocked","recommended","challenge_recommended","allowed","manual_block","ignored","warned"]},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"summary":{"type":"string","description":"for a path farm finding, e.g. 'one-try-per-IP farm on /login: 40 of 42 addresses that tried to sign in did it once and never succeeded (95.2 %)'"},"evidence":{"type":"array","description":"for a path farm finding, up to 5 strings like '<n> of <all> addresses that <clause> (<share> %); <arm>', e.g. '40 of 42 addresses that tried to sign in did it once and never succeeded (95.2 %); no baseline yet, alerting above 5.0 %'"},"run_id":{"type":"string"},"private_address":{"type":"boolean","description":"key is a loopback/private/reserved IP (a proxy or tunnel, not a visitor); such rows are never recommended or blocked"},"session_ip":{"oneOf":[{"type":"string","description":"for a session seen from exactly one IP, that IP (the dashboard files the session under it); null otherwise"},{"type":"null"}]},"ip_count":{"oneOf":[{"type":"integer","minimum":0,"maximum":500,"description":"for key_type 'cfp', the distinct contributing IPs (IPv6 collapsed to /64) of its greylist row at query time; 500 is the cap and means 500 or more; null for every other key type or when unknown"},{"type":"null"}]},"alerted":{"type":"array","items":{"$ref":"#/components/schemas/AlertedChip"},"description":"where this finding was alerted (ALRT-08); empty when it never was"}},"required":["id","ts","key_type","key","tap","verdict","category","confidence","action","expires","summary","evidence","run_id","private_address","session_ip","alerted"]},"Rollup":{"type":"object","properties":{"hour":{"type":"integer","description":"epoch milliseconds"},"requests":{"type":"integer"},"blocked":{"type":"integer"},"beacons":{"type":"integer"},"grey":{"type":"integer"},"investigated":{"type":"integer"},"recommended":{"type":"integer"},"by_category":{"type":"object","additionalProperties":{"type":"integer"}},"polls":{"type":"integer"},"bytes":{"type":"integer"},"challenged":{"type":"integer","description":"served browser challenge pages (a proof-of-work page for a page load); an API call under a challenge counts in challenge_api instead"},"challenge_api":{"type":"integer","description":"API calls under a challenge: passed to the app with an x-camada-challenge: 1 response header instead of a page (a 403 refusal from an older SDK counts here too)"},"challenge_passed":{"type":"integer"}},"required":["hour","requests","blocked","beacons","grey","investigated","recommended","by_category"]},"AllowItem":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","path","country","asn","header"]},"key":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"},"reason":{"type":"string"},"author":{"oneOf":[{"type":"string"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["key_type","key","reason","author","created","expires"]},"BlockItem":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","path","country","asn","header"]},"key":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"},"action":{"type":"string","enum":["block","challenge"]},"reason":{"type":"string"},"source":{"type":"string","enum":["verdict","manual","rule"]},"author":{"oneOf":[{"type":"string"},{"type":"null"}]},"verdict_id":{"oneOf":[{"type":"string"},{"type":"null"}]},"rule_id":{"oneOf":[{"type":"string"},{"type":"null"}]},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"}},"required":["key_type","key","action","reason","source","author","verdict_id","expires","created"]},"RulesConfig":{"type":"object","properties":{"rules":{"type":"object","additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean"},"alert":{"type":"boolean"}},"required":["enabled","alert"]}},"categories":{"type":"object","additionalProperties":{"type":"string","enum":["block","challenge","warn","alert","ignore"]}},"sensitivity":{"type":"string","enum":["conservative","balanced","aggressive"]}},"required":["rules","categories","sensitivity"]},"Condition":{"type":"object","properties":{"field":{"type":"string","enum":["ip","asn","country","tlsx","path","ua","header","bot.verified","rule"]},"op":{"type":"string","enum":["is","is_not","is_in","not_in","starts_with","matches","contains","fired"]},"value":{"description":"a string, a number, a boolean, or a list of up to 500 of them for is_in / not_in. A path value ignores case and a trailing slash"},"name":{"type":"string","pattern":"^[A-Za-z0-9-]{1,64}$","description":"header conditions only: which header to read, matched case-insensitively"}},"required":["field","op","value"]},"Stack":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"group":{"type":"string"},"source":{"type":"string","enum":["user","inferred"]},"confidence":{"type":"number"},"evidence":{"oneOf":[{"type":"string"},{"type":"null"}]},"updated_at":{"type":"integer","description":"epoch milliseconds"}},"required":["id","label","group","source","confidence","evidence","updated_at"]}},"dismissed":{"type":"array","items":{"type":"string"},"description":"product ids the customer removed; inference cannot bring them back"},"vocabulary":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"group":{"type":"string"}},"required":["id","label","group"]}}},"required":["products","dismissed","vocabulary"]},"CustomRule":{"type":"object","properties":{"id":{"type":"string","description":"cr_<12 hex>, or builtin:allow / builtin:block"},"name":{"type":"string","maxLength":80},"enabled":{"type":"boolean"},"position":{"type":"integer","description":"order is precedence: first match wins"},"action":{"type":"string","enum":["skip","block","challenge","warn"]},"record":{"type":"boolean","description":"skip only: true (the default) scores the entity and shows it as Allowed, false drops it before scoring"},"conds":{"type":"array","items":{"$ref":"#/components/schemas/Condition"},"maxItems":20},"reason":{"type":"string","maxLength":500},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"author":{"oneOf":[{"type":"string"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"updated":{"type":"integer","description":"epoch milliseconds"},"builtin":{"oneOf":[{"type":"string","enum":["allow","block"]},{"type":"null"}]},"entries":{"type":"integer","description":"built-ins only: how many allow-list / manual-block entries the rule holds"},"request_enforceable":{"type":"boolean","description":"every condition is one an SDK can judge per request, so the rule rides snapshot v5"},"ip_unreadable":{"type":"array","minItems":1,"items":{"type":"string"},"description":"present only when the rule holds ip values the analyst cannot read (legacy rows stored before validation); such a rule never fires, on the analyst or in an SDK, until it is edited"},"regex_refused":{"type":"array","minItems":1,"items":{"type":"string"},"description":"present only when the rule holds matches patterns the edge regex engine refuses; such a rule never matches, on the analyst or in an SDK, until it is edited"}},"required":["id","name","enabled","position","action","record","conds","reason","expires","created","updated","builtin","request_enforceable"]},"RuleGroup":{"type":"object","properties":{"id":{"type":"string","description":"the catalog category"},"name":{"type":"string"},"description":{"type":"string"}},"required":["id","name","description"]},"RulesPatch":{"type":"object","properties":{"rules":{"type":"object","additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean"},"alert":{"type":"boolean"}},"required":[]}},"categories":{"type":"object","additionalProperties":{"type":"string","enum":["block","challenge","warn","alert","ignore"]}},"sensitivity":{"type":"string","enum":["conservative","balanced","aggressive"]}},"required":[],"minProperties":1},"CatalogRule":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"weight":{"type":"number"},"scope":{"type":"array","items":{"type":"string","description":"key type the rule scores"}},"requires":{"type":"array","items":{"type":"string","description":"capability names from edge-analyst capabilities.js"}},"category":{"type":"string"},"observable":{"type":"boolean","description":"some tap this project has sent covers every capability this rule requires (and the beacon is on when it needs one); single entities seen through a weaker tap are still skipped"}},"required":["id","description","weight","scope","requires","category","observable"]},"Rules":{"allOf":[{"$ref":"#/components/schemas/RulesConfig"},{"type":"object","properties":{"catalog":{"type":"array","items":{"$ref":"#/components/schemas/CatalogRule"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/RuleGroup"},"description":"RULES-05: the catalog categories, as the managed-rule groups the page renders"},"enforceable_by":{"type":"object","properties":{"ip":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"cidr":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"path":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"asn":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"country":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"tlsx":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"ua":{"oneOf":[{"type":"boolean"},{"type":"null"}]},"header":{"oneOf":[{"type":"boolean"},{"type":"null"}]}},"required":["ip","cidr","path","asn","country","tlsx","ua","header"],"description":"null: nothing observed yet that decides this axis — the six tap axes until a tap sent an event or a beacon, ua/header until the analyst has any freshness row (SDK-G12)."}},"required":["catalog","groups","enforceable_by"]}]},"AuditEntry":{"type":"object","properties":{"ts":{"type":"integer","description":"epoch milliseconds"},"actor":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"},"email":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["id","email"]},{"const":"system"}]},"action":{"type":"string"},"target":{"type":"string"},"before":{},"after":{},"ip":{"oneOf":[{"type":"string"},{"type":"null"}]},"ua":{"oneOf":[{"type":"string"},{"type":"null"}]},"via":{"oneOf":[{"type":"object","additionalProperties":true,"description":"how the change was made when it was not a plain click: { agent, conversation_id, proposal_id } and/or { impersonation: { reason, until } }"},{"type":"null"}]}},"required":["ts","actor","action","target","before","after","ip","ua","via"]},"Plan":{"type":"object","properties":{"id":{"type":"string","enum":["free","starter","growth","scale"]},"name":{"type":"string"},"price_usd_month":{"oneOf":[{"type":"number"},{"type":"null"}]},"included_events":{"oneOf":[{"type":"integer"},{"type":"null"}]},"projects":{"type":"integer","description":"-1 = unlimited"},"retention_days":{"type":"integer"},"enforcement":{"type":"boolean"},"alerts":{"type":"array","items":{"type":"string","enum":["email","slack","webhook"]}},"chat":{"type":"boolean"},"audit_log":{"type":"boolean"},"overage_usd_per_million":{"oneOf":[{"type":"number"},{"type":"null"}]},"chat_tokens_month":{"oneOf":[{"type":"integer","description":"D28: null = no chat, -1 = unlimited, else input+output tokens per calendar month"},{"type":"null"}]}},"required":["id","name","price_usd_month","included_events","projects","retention_days","enforcement","alerts","chat","audit_log","overage_usd_per_million","chat_tokens_month"]},"PlansDoc":{"type":"object","properties":{"unit":{"type":"string"},"placeholder_prices":{"type":"boolean"},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}},"trial_days":{"type":"integer"},"trial_plan":{"type":"string"},"trial_plans":{"type":"array","items":{"type":"string"}}},"required":["unit","placeholder_prices","plans","trial_days","trial_plan","trial_plans"]},"Period":{"type":"object","properties":{"start":{"type":"integer","description":"epoch milliseconds"},"end":{"type":"integer","description":"epoch milliseconds"},"source":{"type":"string","enum":["stripe","calendar"]}},"required":["start","end","source"]},"UsageSummary":{"type":"object","properties":{"included_events":{"oneOf":[{"type":"integer","description":"null on Enterprise: no cap"},{"type":"null"}]},"used_events":{"type":"integer","description":"counted against the current allowance: since the last upgrade applied in this period, else the whole period (camada-all-69l0)"},"period_used_events":{"type":"integer","description":"the whole period's usage; equals used_events with no upgrade in the period"},"counted_from":{"oneOf":[{"type":"integer","description":"when the last upgrade applied in this period was made (used_events counts from it), or null"},{"type":"null"}]},"settled":{"type":"array","description":"overage billed at an upgrade in this period, on that upgrade's own invoice at the previous plan's rate; not in next_invoice_usd","items":{"type":"object","properties":{"at":{"type":"integer","description":"epoch milliseconds"},"plan":{"type":"string","enum":["free","starter","growth","scale"]},"events":{"type":"integer"},"usd":{"type":"number","description":"Stripe's invoice line; an estimate while pending and not read back yet"},"state":{"type":"string","enum":["pending","applied"]}},"required":["at","plan","events","usd","state"]}},"projected_events":{"type":"integer","description":"used_events extrapolated over [counted_from ?? period start, period end); used_events as is in its first hour"},"overage_events":{"type":"integer"},"overage_usd":{"type":"number"},"next_invoice_usd":{"type":"number","description":"what Stripe's next invoice will bill: the base charged in advance (the subscription's own amount, a retired price included; the pending plan's catalog price, with a downgrade pending) plus this period's overage at the catalog rate, less, from 10 minutes before the period end on, the overage not sent to Stripe yet: Stripe bills that on the invoice after (the next period shows it as carried_over_events); a card trial: what its Stripe price charges, due at trial_end; 0 for a trial that is cancelling or has no card; for a subscription cancelling at the period end, the final invoice: the overage only"},"carried_over_events":{"type":"integer","description":"overage units an earlier period used after its invoice closed, billed on this one"},"uncharged_events":{"type":"integer","description":"units over the allowance no invoice will bill: a subscription cancelling at the period end, used after its last meter send; on Free or a trial, all of it; not in overage_events. Assumes a subscription cancelled immediately in Stripe was cancelled with invoice_now (docs/runbook.md §6): without it the overage already sent for the cut period is billed nowhere and shown nowhere"}},"required":["included_events","used_events","period_used_events","counted_from","settled","projected_events","overage_events","overage_usd","next_invoice_usd","carried_over_events","uncharged_events"]},"TrialStart":{"type":"object","properties":{"plan":{"type":"string","enum":["free","starter","growth","scale"]},"can_start_trial":{"type":"boolean"},"reason":{"oneOf":[{"type":"string","enum":["email_unverified","trial_used","already_subscribed","not_admin","impersonation_no_trial"]},{"type":"null"}]},"status":{"type":"string","enum":["trialing","active","past_due","canceled","free"],"description":"deprecated (legacy sunset): free for a new org"},"trial_end":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}],"description":"deprecated (legacy sunset): null for a new org"},"trial_used_elsewhere":{"type":"boolean","description":"deprecated (legacy sunset): the creator (by user id or verified email) had already used their one trial"},"trial_awaiting_verification":{"type":"boolean","description":"deprecated (legacy sunset): the creator's email was not verified when the org was created"}},"required":["plan","can_start_trial","reason","status","trial_end","trial_used_elsewhere","trial_awaiting_verification"]},"Billing":{"type":"object","properties":{"plan":{"type":"string","enum":["free","starter","growth","scale"]},"status":{"type":"string","enum":["trialing","active","past_due","canceled","free"]},"period":{"$ref":"#/components/schemas/Period"},"cancel_at_period_end":{"type":"boolean"},"cancel_at":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}],"description":"camada-all-c65p: when Stripe will end a live subscription that is not cancelling at the period end (a custom end date after it); null otherwise. The current period still renews."},"pending_plan":{"oneOf":[{"type":"string","enum":["free","starter","growth","scale"]},{"type":"null"}]},"trial_end":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"trial_used_elsewhere":{"type":"boolean","description":"camada-all-sbsl: the org started on Free because its creator had already used their one trial (in another org, by user id or email)"},"trial_awaiting_verification":{"type":"boolean","description":"camada-all-sbsl: the org is on Free waiting for its creator to verify their email; the creator then enters the emailed code and starts the trial with POST /billing/trial"},"can_start_trial":{"type":"boolean","description":"Equals trial_offer.can_start: this caller may start a card trial now (an admin, not impersonating, org and user eligible)"},"trial_offer":{"type":"object","properties":{"can_start":{"type":"boolean"},"reason":{"oneOf":[{"type":"string","enum":["not_admin","impersonation_no_trial","already_subscribed","trial_used","email_unverified"]},{"type":"null"}]},"plans":{"type":"array","items":{"type":"string","enum":["starter","growth"]}},"days":{"type":"integer"}},"required":["can_start","reason","plans","days"]},"trial_card":{"oneOf":[{"type":"object","properties":{"brand":{"type":"string"},"last4":{"type":"string"}},"required":["brand","last4"]},{"type":"null"}],"description":"camada-all-fqd5: the card behind a Stripe trial; null otherwise"},"trial_charge":{"oneOf":[{"type":"object","properties":{"usd":{"type":"number"},"at":{"type":"integer","description":"epoch milliseconds"}},"required":["usd","at"]},{"type":"null"}],"description":"camada-all-fqd5: what a Stripe trial charges when it ends (what the subscription's monthly price bills, a retired price included) and when; null when it is cancelling or not a Stripe trial"},"unpaid_plan":{"oneOf":[{"type":"string","enum":["free","starter","growth","scale"]},{"type":"null"}],"description":"camada-all-fqd5: the plan whose first invoice is still unpaid (effective plan is Free meanwhile); null otherwise"},"trial_rejected":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string","enum":["card_used","card_prepaid","no_fingerprint","trial_used"]},"at":{"type":"integer","description":"epoch milliseconds"},"plan":{"oneOf":[{"type":"string","enum":["starter","growth"]},{"type":"null"}]}},"required":["reason","at","plan"]},{"type":"null"}],"description":"camada-all-jqwq: the last trial Checkout we refused, and the plan it was for (the subscription was canceled, nothing charged). Cleared by a new trial Checkout, a grant or a paid Checkout"},"checkout_trial":{"oneOf":[{"type":"object","properties":{"plan":{"type":"string","enum":["starter","growth"]},"expires":{"type":"integer","description":"epoch milliseconds"}},"required":["plan","expires"]},{"type":"null"}],"description":"camada-all-jqwq: the org's stored open trial Checkout (drives \"Finish starting your trial\"); null otherwise. No URL"},"payment_action_required":{"oneOf":[{"type":"object","properties":{"invoice_id":{"oneOf":[{"type":"string"},{"type":"null"}]},"total_usd":{"oneOf":[{"type":"number"},{"type":"null"}]},"hosted_url":{"oneOf":[{"type":"string"},{"type":"null"}]},"at":{"type":"integer","description":"epoch milliseconds"},"attempt":{"oneOf":[{"type":"integer"},{"type":"null"}],"description":"Stripe invoice attempt_count of the action; absent on rows written before it"}},"required":["invoice_id","total_usd","hosted_url","at"]},{"type":"null"}],"description":"Set by invoice.payment_action_required: the customer must confirm a payment. hosted_url (the invoice page) is given to admins only, null for members; cleared by that invoice being paid, the subscription ending, that invoice being voided or marked uncollectible, or a decline of a later payment attempt, or one whose PaymentIntent is declined; null while the org is on the free plan with its subscription kept (Stripe unpaid or paused): the prompt is stored but hidden, and returns if Stripe restores the subscription while that invoice is still open; the open invoice stays payable from Invoices; the banner and the payment notices are dropped when the org moves to another subscription"},"usage":{"$ref":"#/components/schemas/UsageSummary"},"gates":{"type":"object","properties":{"projects":{"type":"object","properties":{"limit":{"type":"integer","description":"-1 = unlimited"},"used":{"type":"integer"}},"required":["limit","used"]},"retention_days":{"type":"integer"},"alert_channels":{"type":"array","items":{"type":"string","enum":["email","slack","webhook"]}},"chat":{"type":"boolean"},"enforcement":{"type":"boolean"},"audit_log":{"type":"boolean"}},"required":["projects","retention_days","alert_channels","chat","enforcement","audit_log"]},"stripe":{"type":"object","properties":{"customer":{"type":"boolean"},"portal":{"type":"boolean"}},"required":["customer","portal"]}},"required":["plan","status","period","cancel_at_period_end","cancel_at","pending_plan","trial_end","trial_used_elsewhere","trial_awaiting_verification","can_start_trial","trial_offer","trial_card","trial_charge","unpaid_plan","trial_rejected","checkout_trial","payment_action_required","usage","gates","stripe"]},"Invoice":{"type":"object","properties":{"id":{"type":"string"},"number":{"oneOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string"},"created":{"type":"integer","description":"epoch milliseconds"},"total_usd":{"type":"number"},"subscription":{"oneOf":[{"type":"string","description":"the Stripe subscription the invoice bills"},{"type":"null"}]},"amount_due_usd":{"type":"number","description":"what is still due (Stripe amount_remaining, else amount_due, else total); meaningful only while status is open"},"owed":{"type":"boolean","description":"open, money due, on the org's current subscription and not settled here; false otherwise. A healthy org's open renewal is owed too: whether the org is held is `status` on billing. The row is listed either way"},"currency":{"type":"string"},"hosted_url":{"oneOf":[{"type":"string"},{"type":"null"}]},"pdf_url":{"oneOf":[{"type":"string"},{"type":"null"}]},"lines":{"type":"array","items":{"type":"object","properties":{"description":{"type":"string"},"quantity":{"oneOf":[{"type":"integer"},{"type":"null"}]},"amount_usd":{"type":"number"}},"required":["description","quantity","amount_usd"]}}},"required":["id","number","status","created","total_usd","subscription","amount_due_usd","owed","currency","hosted_url","pdf_url","lines"]},"UsageItem":{"type":"object","properties":{"project_id":{"type":"string"},"day":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"UTC day"},"events":{"type":"integer"},"polls":{"type":"integer"},"bytes":{"type":"integer"}},"required":["project_id","day","events","polls","bytes"]},"ChatBlock":{"description":"One piece of an assistant message, in the order it happened","oneOf":[{"type":"object","properties":{"type":{"const":"text"},"text":{"type":"string"}},"required":["type","text"]},{"type":"object","properties":{"type":{"const":"tool"},"id":{"type":"string"},"name":{"type":"string"},"input_summary":{"type":"string"},"ok":{"type":"boolean"},"summary":{"type":"string"},"latency_ms":{"type":"integer"}},"required":["type","id","name","input_summary","ok","summary","latency_ms"]},{"type":"object","properties":{"type":{"const":"proposal"},"proposal_id":{"type":"string"}},"required":["type","proposal_id"]},{"type":"object","properties":{"type":{"const":"remembered"},"fact_id":{"type":"string"}},"required":["type","fact_id"]},{"type":"object","properties":{"type":{"const":"citations"},"verdict_ids":{"type":"array","items":{"type":"string"}},"run_ids":{"type":"array","items":{"type":"string"}},"entities":{"type":"array","items":{"$ref":"#/components/schemas/FactEntity"}}},"required":["type","verdict_ids","run_ids","entities"]},{"type":"object","properties":{"type":{"const":"workflow_result"},"workflow":{"type":"string"},"data":{}},"required":["type","workflow","data"]}]},"ChatConversation":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"created":{"type":"integer","description":"epoch milliseconds"},"updated":{"type":"integer","description":"epoch milliseconds"},"last_message_at":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"message_count":{"type":"integer"},"cost_usd":{"type":"number"}},"required":["id","title","created","updated","last_message_at","message_count","cost_usd"]},"ChatMessage":{"type":"object","properties":{"id":{"type":"string"},"seq":{"type":"integer"},"role":{"type":"string","enum":["user","assistant"]},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/ChatBlock"}},"model":{"oneOf":[{"type":"string"},{"type":"null"}]},"fallback":{"type":"boolean"},"prompt_version":{"oneOf":[{"type":"string"},{"type":"null"}]},"tools_version":{"oneOf":[{"type":"string"},{"type":"null"}]},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"cost_usd":{"type":"number"},"latency_ms":{"oneOf":[{"type":"integer"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"}},"required":["id","seq","role","blocks","model","fallback","prompt_version","tools_version","input_tokens","output_tokens","cost_usd","latency_ms","created"]},"ChatProposal":{"type":"object","properties":{"id":{"type":"string"},"conversation_id":{"type":"string"},"message_id":{"type":"string"},"kind":{"type":"string","enum":["allow","block","unblock","custom_rule","custom_rule_delete","rule","category","sensitivity","config","alert_route","remember"]},"payload":{},"diff":{"type":"object","properties":{"before":{},"after":{}},"required":[]},"reason":{"type":"string"},"status":{"type":"string","enum":["pending","confirmed","rejected","failed","expired"]},"created":{"type":"integer","description":"epoch milliseconds"},"decided_at":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"decided_by":{"oneOf":[{"type":"string"},{"type":"null"}]},"result":{}},"required":["id","conversation_id","message_id","kind","payload","diff","reason","status","created","decided_at","decided_by","result"]},"ChatBudget":{"type":"object","properties":{"tokens":{"oneOf":[{"type":"integer","description":"null = the plan has no chat, -1 = unlimited"},{"type":"null"}]},"used":{"type":"integer"},"remaining":{"oneOf":[{"type":"integer","description":"null when unlimited"},{"type":"null"}]}},"required":["tokens","used","remaining"]},"StaffOrg":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"plan":{"type":"string","enum":["free","starter","growth","scale"]},"members":{"type":"integer"},"projects":{"type":"integer"},"staff":{"type":"boolean","description":"true for the camada staff organization itself, which cannot be impersonated"},"created":{"type":"integer","description":"epoch milliseconds"}},"required":["id","name","slug","plan","members","projects","staff","created"]},"Impersonation":{"type":"object","properties":{"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"reason":{"type":"string"},"started":{"type":"integer","description":"epoch milliseconds"},"until":{"type":"integer","description":"epoch milliseconds"}},"required":["org","reason","started","until"]},"FactKind":{"type":"string","enum":["preference","infrastructure","decision","contact","note"]},"FactSource":{"type":"string","enum":["user","agent","system"]},"FactEntity":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","asn","path","country"]},"key":{"type":"string"}},"required":["key_type","key"]},"MemoryFact":{"type":"object","properties":{"id":{"type":"string"},"kind":{"$ref":"#/components/schemas/FactKind"},"text":{"type":"string","maxLength":500},"source":{"$ref":"#/components/schemas/FactSource"},"author":{"oneOf":[{"type":"object","properties":{"id":{"oneOf":[{"type":"string"},{"type":"null"}]},"email":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["id","email"]},{"const":"system"},{"const":"agent"}],"description":"the person for a fact someone typed; the literal source for the two the app writes itself"},"entity":{"oneOf":[{"$ref":"#/components/schemas/FactEntity"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"updated":{"type":"integer","description":"epoch milliseconds"},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["id","kind","text","source","author","entity","created","updated","expires"]},"MemoryDoc":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["fact","conversation","run"]},"ref":{"type":"string","description":"the fact / conversation / run id this indexes"},"text":{"type":"string"},"embedded_at":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"model":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","ref","text"]},"AlertSeverity":{"type":"string","enum":["info","warning","critical"]},"AlertDeliveryStatus":{"type":"string","enum":["queued","sent","failed","digested","dropped","muted","plan_excluded"]},"AlertPayload":{"type":"object","properties":{"schema_version":{"const":1},"event":{"type":"string"},"severity":{"$ref":"#/components/schemas/AlertSeverity"},"occurred_at":{"type":"string","format":"date-time","description":"ISO 8601 — the one non-ms time in this API, because receivers read it"},"delivery_id":{"type":"string"},"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"env":{"type":"string"}},"required":["id","name","slug","env"]},"entity":{"oneOf":[{"type":"object","properties":{"key_type":{"type":"string"},"key":{"type":"string"}},"required":["key_type","key"]},{"type":"null"}]},"verdict":{"oneOf":[{"type":"string"},{"type":"null"}]},"category":{"oneOf":[{"type":"string"},{"type":"null"}]},"confidence":{"oneOf":[{"type":"number"},{"type":"null"}]},"action":{"oneOf":[{"type":"string"},{"type":"null"}]},"summary":{"oneOf":[{"type":"string"},{"type":"null"}]},"evidence":{"type":"array"},"links":{"type":"object","properties":{"entity":{"oneOf":[{"type":"string"},{"type":"null"}]},"run":{"oneOf":[{"type":"string"},{"type":"null"}]},"allow":{"oneOf":[{"type":"string"},{"type":"null"}],"description":"null when there is no entity, or for a path farm alert (verdict.suspicious on a key_type path entity), which is alert only"},"block":{"oneOf":[{"type":"string"},{"type":"null"}],"description":"null when there is no entity, or for a path farm alert (verdict.suspicious on a key_type path entity), which is alert only"},"project":{"type":"string"}},"required":["entity","run","allow","block","project"]},"data":{"type":"object","additionalProperties":true}},"required":["schema_version","event","severity","occurred_at","delivery_id","org","project","entity","verdict","category","confidence","action","summary","evidence","links","data"]},"AlertCatalogEntry":{"type":"object","properties":{"id":{"type":"string"},"default_severity":{"$ref":"#/components/schemas/AlertSeverity"},"description":{"type":"string"},"example":{"$ref":"#/components/schemas/AlertPayload"}},"required":["id","default_severity","description","example"]},"AlertChannel":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["slack","webhook","email"]},"name":{"type":"string"},"config_masked":{"type":"string","description":"never the secret: https://hooks.slack.com/…/T0…/•••Z1a2"},"enabled":{"type":"boolean"},"cap_per_hour":{"type":"integer"},"failing_since":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"disabled_reason":{"oneOf":[{"type":"string"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"plan_excluded":{"type":"boolean","description":"computed: the org's plan does not include this channel type, so alerts to it are skipped (delivery status plan_excluded) until an upgrade"}},"required":["id","type","name","config_masked","enabled","cap_per_hour","failing_since","disabled_reason","created","plan_excluded"]},"AlertRoute":{"type":"object","properties":{"event":{"type":"string"},"channel_id":{"type":"string"},"severity":{"$ref":"#/components/schemas/AlertSeverity"},"mode":{"type":"string","enum":["immediate","hourly","daily"]},"enabled":{"type":"boolean"}},"required":["event","channel_id","severity","mode","enabled"]},"AlertDelivery":{"type":"object","properties":{"id":{"type":"string"},"event":{"type":"string"},"ts":{"type":"integer","description":"epoch milliseconds"},"status":{"$ref":"#/components/schemas/AlertDeliveryStatus"},"attempt":{"type":"integer"},"response_code":{"oneOf":[{"type":"integer"},{"type":"null"}]},"latency_ms":{"oneOf":[{"type":"integer"},{"type":"null"}]},"error":{"oneOf":[{"type":"string"},{"type":"null"}]},"digest_of":{"oneOf":[{"type":"integer"},{"type":"null"}]},"digest_mode":{"oneOf":[{"type":"string","enum":["hourly","daily"]},{"type":"null"}]},"digest_id":{"oneOf":[{"type":"string"},{"type":"null"}]},"channel":{"type":"object","properties":{"id":{"type":"string"},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"type":{"oneOf":[{"type":"string"},{"type":"null"}]},"deleted":{"type":"boolean"}},"required":["id","name","type","deleted"]}},"required":["id","event","ts","status","attempt","response_code","latency_ms","error","digest_of","digest_mode","digest_id","channel"]},"Notification":{"type":"object","properties":{"id":{"type":"string"},"ts":{"type":"integer","description":"epoch milliseconds"},"org_id":{"type":"string"},"project_id":{"type":"string"},"type":{"type":"string","description":"a catalog event id, or trial.ended / trial.started / trial.rejected / payment.action_required / channel.disabled / billing.duplicate_subscription"},"data":{"type":"object","additionalProperties":true},"read":{"type":"boolean"}},"required":["id","ts","org_id","project_id","type","data","read"]},"AlertedChip":{"type":"object","properties":{"channel_id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"ts":{"type":"integer","description":"epoch milliseconds"},"digest_id":{"oneOf":[{"type":"string","description":"the digest delivery that carried a held (`digested`) row; null while it waits and on every other status"},{"type":"null"}]},"deleted":{"type":"boolean","description":"the channel was deleted; name and type are its last ones"}},"required":["channel_id","name","type","status","ts","digest_id","deleted"]},"Ok":{"type":"object","properties":{"ok":{"const":true}},"required":["ok"]}},"securitySchemes":{"session":{"type":"apiKey","in":"cookie","name":"camada.session_token","description":"Better Auth session; on https the cookie is named __Secure-camada.session_token."}}},"paths":{"/health":{"get":{"summary":"Liveness","tags":["auth"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/status":{"get":{"summary":"Public component status for the marketing site (OPS-04); CORS allows exactly MKT_URL; cached 30 s, 60/min per IP","tags":["auth"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"components":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["www","app","ingest"]},"ok":{"type":"boolean"},"last_checked":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"uptime_24h":{"type":"number","description":"0..1 over the last 24 h"},"p95_ms":{"oneOf":[{"type":"integer"},{"type":"null"}]}},"required":["id","ok","last_checked","uptime_24h","p95_ms"]}},"window_hours":{"type":"integer"}},"required":["components","window_hours"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]},"options":{"summary":"CORS preflight for the status page","tags":["auth"],"responses":{"204":{"description":"OK","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/openapi.json":{"get":{"summary":"This document","tags":["auth"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/client-errors":{"post":{"summary":"A browser error from the web app, reported to error tracking as service web. No session required (one, when present, names the user); 16 KB body, 30/min per IP","tags":["auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","minLength":1,"maxLength":200},"message":{"type":"string","maxLength":2000},"stack":{"type":"string","maxLength":8000},"path":{"type":"string","maxLength":1000},"route":{"type":"string","maxLength":500},"release":{"type":"string","maxLength":100},"breadcrumbs":{"type":"array","maxItems":20,"items":{"type":"object","properties":{"kind":{"type":"string","enum":["nav","api"]},"at":{"type":"number"},"path":{"type":"string","maxLength":1000},"method":{"type":"string","maxLength":10},"status":{"type":"integer"}},"required":["kind","at","path"]}}},"required":["type","message"]}}}},"responses":{"204":{"description":"OK","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/login":{"get":{"summary":"Redirect to the app sign-in page (APP_URL/sign-in?return_to=), carrying utm_* over","tags":["auth"],"parameters":[{"name":"return_to","in":"query","required":false,"schema":{"type":"string","description":"same-origin path to return to"}}],"responses":{"302":{"description":"Redirect"},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/link/sign-in":{"post":{"summary":"Complete an emailed sign-in link. The mail carries the token only in the URL fragment (APP_URL/sign-in/link?return_to=#token=); the page POSTs it here. 200 {redirect} is a same-app path: the return_to, /sign-in/two-factor?return_to= for a 2FA user (no session), or /sign-in?return_to=&error=INVALID_TOKEN for a bad or used link; the session cookie is set. Origin must be APP_URL; 10/min per IP","tags":["auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":2048},"returnTo":{"type":"string","description":"same-origin path; anything else becomes /"}},"required":["token"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"redirect":{"type":"string"}},"required":["redirect"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/link/verify-email":{"post":{"summary":"Complete an emailed verify-email link (APP_URL/verify-email?return_to=#token=). 200 {redirect}: /sign-in?return_to=&notice=email_verified (a session is set, or the 2FA challenge path), or the same with &error=TOKEN_EXPIRED|INVALID_TOKEN. &link=other is appended (no error) when the link was already used (its address was verified before this call) and the request is signed in as a different account: nothing changed, the session is still that other account. Origin must be APP_URL; 10/min per IP","tags":["auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":2048},"returnTo":{"type":"string","description":"same-origin path; anything else becomes /"}},"required":["token"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"redirect":{"type":"string"}},"required":["redirect"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/link/check-reset":{"post":{"summary":"Whether an emailed reset-password token (APP_URL/reset-password#token=) is still live. Never consumes it; the reset itself is POST /api/auth/reset-password {token, newPassword}. Origin must be APP_URL; 10/min per IP","tags":["auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":2048}},"required":["token"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"}},"required":["valid"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/magic-link/verify":{"get":{"summary":"Old emailed link (retired; all expired): 302 to APP_URL/sign-in?error=INVALID_TOKEN. Carries no token, reads nothing, sets no cookie","tags":["auth"],"responses":{"302":{"description":"Redirect"},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/verify-email":{"get":{"summary":"Old emailed link (retired; all expired): 302 to APP_URL/sign-in?error=INVALID_TOKEN. Carries no token, reads nothing, sets no cookie","tags":["auth"],"responses":{"302":{"description":"Redirect"},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/reset-password/{token}":{"get":{"summary":"Old emailed link (retired; all expired): 302 to APP_URL/sign-in?error=INVALID_TOKEN. Carries no token, reads nothing, sets no cookie","tags":["auth"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect"},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/auth/logout":{"post":{"summary":"Delete the session, clear the cookie and redirect to APP_URL; works without a cookie","tags":["auth"],"responses":{"302":{"description":"Redirect"},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/stripe/webhook":{"post":{"summary":"Stripe webhook: Stripe-Signature verified with STRIPE_WEBHOOK_SECRET inside a 5 minute tolerance, processed once per event id, plan synced to edge-analyst before the 200","tags":["auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"a Stripe Event; the raw bytes are what the signature covers","additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"duplicate":{"type":"boolean"}},"required":["ok"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/plans":{"get":{"summary":"Plan catalog and trial length (public; the same document camada-mkt renders)","tags":["auth"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlansDoc"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/me":{"get":{"summary":"Current user, orgs, last org/project, client IP, and `expired_invites`: invitations to orgs the user is not in that expired within the last 30 days (empty while impersonating), and `joined`: orgs the user joined by accepting an invitation and has not acknowledged yet, newest first (empty while impersonating), and `removed_from`: orgs an admin removed the user from in the last 30 days (not orgs they left themselves or are back in), newest first (empty while impersonating)","tags":["orgs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"orgs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["id","name","slug","role"]}},"last":{"oneOf":[{"type":"object","properties":{"org_id":{"type":"string"},"project_id":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["org_id","project_id"]},{"type":"null"}]},"expired_invites":{"type":"array","items":{"type":"object","properties":{"org":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"expired_at":{"type":"integer","description":"epoch milliseconds"}},"required":["org","expired_at"]}},"joined":{"type":"array","items":{"type":"object","properties":{"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["org","role"]}},"removed_from":{"type":"array","items":{"type":"object","properties":{"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"at":{"type":"integer","description":"epoch milliseconds"}},"required":["org","at"]}},"client_ip":{"type":"string"},"staff":{"type":"boolean"},"impersonation":{"oneOf":[{"$ref":"#/components/schemas/Impersonation"},{"type":"null"}]},"impersonation_ended":{"oneOf":[{"type":"object","properties":{"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"started":{"type":"integer","description":"epoch milliseconds"},"ended_at":{"type":"integer","description":"epoch milliseconds"}},"required":["org","started","ended_at"]},{"type":"null"}]}},"required":["user","orgs","last","expired_invites","joined","removed_from","client_ip","staff","impersonation","impersonation_ended"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/me/joined/{org_id}":{"delete":{"summary":"Acknowledge a `joined` entry: clears the caller's own flag for that org. Idempotent: 204 whether or not it was set","tags":["orgs"],"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"OK","content":{"application/json":{"schema":{}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs":{"post":{"summary":"Create an organization (one D1 batch: the org, its admin membership and its Free subscription)","tags":["orgs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80}},"required":["name"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"org":{"$ref":"#/components/schemas/Org"},"start":{"$ref":"#/components/schemas/TrialStart"}},"required":["org","start"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden; code ∈ impersonation_no_org_create (staff impersonating an org: end the impersonation first)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"org_limit_reached: the caller has already created 5 organizations (camada staff are exempt)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Only with invite_limit_reached: seconds until another invitation is allowed.","schema":{"type":"integer","minimum":0}}}}}}},"/v1/orgs/{org}":{"get":{"summary":"Organization","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"org":{"$ref":"#/components/schemas/Org"}},"required":["org"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Rename the org / set chat retention (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"chat_retention_days":{"type":"integer","minimum":7,"maximum":365}},"required":[],"minProperties":1}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"org":{"$ref":"#/components/schemas/Org"}},"required":["org"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/admins":{"get":{"summary":"The organization's admins (any role): user_id, name and email, so members and viewers know whom to ask","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string"},"name":{"oneOf":[{"type":"string"},{"type":"null"}]},"email":{"type":"string"}},"required":["user_id","name","email"]}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/members":{"get":{"summary":"Members (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/invites":{"post":{"summary":"Invite by email: camada emails the invitee a sign-in link and the invitation is accepted when they sign in with that address. The email is lower-cased; an invite lives 7 days. A repeat for a pending email with the same role is a resend: the email goes out again, the expiry restarts and it takes a cap slot like a new invitation (200, outcome `resent`). With another role the pending invitation is updated in place (200, outcome `updated`; no new email, no cap slot, the expiry stays). A new invitation is 201, outcome `invited`; so is a repeat for an expired invitation (a fresh invitation with a new invitation_id). At most 50 invitations (new or resent) per org, and 100 per inviting person across all their orgs, per rolling 24 h (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["email","role"]}}}},"responses":{"200":{"description":"A repeat for a pending email: the same invitation resent with a fresh expiry (outcome resent, same role), or with its role changed (outcome updated, another role)","content":{"application/json":{"schema":{"type":"object","properties":{"invitation":{"$ref":"#/components/schemas/Invitation"},"outcome":{"type":"string","enum":["invited","resent","updated"]}},"required":["invitation","outcome"]}}}},"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"invitation":{"$ref":"#/components/schemas/Invitation"},"outcome":{"type":"string","enum":["invited","resent","updated"]}},"required":["invitation","outcome"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_readonly_members (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ already_member (that email already belongs to a member of the org. Checked first, even while an invitation to it is pending. Nothing is sent) | invite_changed (a concurrent request changed this mailbox's invitation first (also on a resend): its org and per-person cap slots stay spent when the email already went out; nothing is audited; reload and retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"invite_limit_reached: 50 invitations from this org, or 100 from the caller across all orgs, in the last 24 h; retry after Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Only with invite_limit_reached: seconds until another invitation is allowed.","schema":{"type":"integer","minimum":0}}}},"502":{"description":"invite_email_failed: the invitation email could not be sent; nothing is created and no cap slot is used, so retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"Pending invites plus ones that expired in the last 30 days (`state` pending or expired) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string","enum":["admin","member","viewer"]},"invitation_id":{"oneOf":[{"type":"string"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"state":{"type":"string","enum":["pending","expired"]},"expires_at":{"type":"string","format":"date-time"}},"required":["email","role","invitation_id","created","state","expires_at"]}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/invites/{email}":{"delete":{"summary":"Revoke an invite (the email matches in any case) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"email","in":"path","required":true,"schema":{"type":"string","description":"invited email"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_readonly_members (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ invite_changed (a concurrent request replaced this mailbox's invitation after it was read: nothing is removed or audited; reload and retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/members/{id}":{"patch":{"summary":"Change a member role (admin+)","tags":["projects"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"user id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["role"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/Member"}},"required":["member"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_readonly_members (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ last_admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Remove a member. Any pending invitation in this org to their email is revoked with them; nothing changes when the removal is refused (admin+)","tags":["projects"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"user id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_readonly_members (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ last_admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/leave":{"post":{"summary":"Leave the organization (any role). Any pending invitation in this org to their email is revoked with them; writes a member.leave audit row. Refused, changing nothing, when the caller is the last admin","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden; code ∈ impersonation_readonly_members (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ last_admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing":{"get":{"summary":"Plan, period, usage against what is included, gates and Stripe state","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/trial":{"post":{"summary":"Verify the CALLER's own email for the trial (D38, verify-only; any admin). Never claims a trial, never writes trialing; the org stays Free and the trial starts only through POST .../billing/trial/checkout. Unverified with no code: mails the caller a code, at most once per 5 minutes per caller, and answers 409 email_unverified with `code_sent` `now`, or `recently` (nothing sent now) with `resend_after` seconds. With `code` (6 digits, checked by Better Auth; wrong or expired: 422 verification_code_invalid): at most 5 codes per caller per 15 minutes, counted before Better Auth sees the code, over that 429 verification_rate_limited with `retry_after` seconds (and Retry-After); an accepted code resets the count; a code is burned after 3 wrong tries (429 verification_rate_limited without Retry-After; the next call without `code` mails a fresh one). Once the caller is verified the org stops waiting for them (trial_awaiting_verification false) and, when this call's code verified the email, a billing.trial_email_verified audit row is written. Answers the `start` shape of POST /v1/orgs (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":64,"description":"the 6-digit verification code emailed to the caller"}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"$ref":"#/components/schemas/TrialStart"}},"required":["start"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_no_trial (staff impersonating the org, with or without a code)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ email_unverified (still unverified; a verification code was emailed now or within the last 5 minutes) | email_unverified_not_sent (still unverified, and the verification code could not be sent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/trial/checkout":{"post":{"summary":"Start a card-required trial (camada-all-jqwq): returns a Stripe Checkout URL (subscription mode, 14 days, no charge today). Checks run in this order and the first failure answers: admin, not impersonating, body, plan in the trial plans (400 plan_not_trialable), consent_version current (409 terms_changed), then the org and caller may start a trial. Nothing is written before Stripe is called. The webhook grants or refuses the trial once the card is entered (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","minLength":1,"maxLength":32},"consent_version":{"type":"string","maxLength":64},"from":{"type":"string","enum":["start","billing"]}},"required":["plan","consent_version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Stripe Checkout"}},"required":["url"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected); or stripe_rejected (Stripe refused the Checkout request as invalid, e.g. a missing Terms of Service URL in the account's Public details)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_no_trial (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ terms_changed (consent_version is not the current trial terms) | already_subscribed | trial_used | email_unverified | checkout_completed | billing_busy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"stripe_error | stripe_unreachable | stripe_auth | stripe_rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"stripe_unconfigured | stripe_price_missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":0}}}}}}},"/v1/orgs/{org}/billing/trial/subscribe":{"post":{"summary":"Subscribe now (camada-all-fqd5): ends a Stripe trial and charges the card at once. A declined or authentication-needing card answers 402 payment_failed and the trial continues; a retry may use a new card. 400 no_trial (legacy or no card trial) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"plan":{"type":"string","enum":["free","starter","growth","scale"]},"status":{"type":"string","enum":["trialing","active","past_due"]}},"required":["ok","plan","status"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_no_charge (staff impersonating the org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ cancel_pending | billing_busy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"stripe_error | stripe_unreachable | stripe_auth | stripe_rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/trial/end":{"post":{"summary":"End the trial (camada-all-fqd5): the org becomes Free now and the Stripe subscription is deleted. Nothing is charged. 400 no_trial (legacy or no card trial) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"plan":{"const":"free"}},"required":["ok","plan"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"stripe_error | stripe_unreachable | stripe_auth | stripe_rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/change/preview":{"get":{"summary":"What upgrading to a plan would charge today (the prorated base-price difference, tax included, overage so far excluded). Read-only: changes nothing in Stripe or D1. 400 not_an_upgrade, no_subscription, contact_sales or trial_no_charge (Stripe still shows a trial; a trial Stripe has converted previews as an upgrade) otherwise (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"plan","in":"query","required":false,"schema":{"type":"string","enum":["free","starter","growth","scale"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["free","starter","growth","scale"]},"from_plan":{"type":"string","enum":["free","starter","growth","scale"]},"amount_due_usd":{"type":"number"},"currency":{"type":"string"},"proration_date":{"type":"integer","description":"Stripe's proration moment; send it back to POST change"}},"required":["plan","from_plan","amount_due_usd","currency","proration_date"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ cancel_pending | change_in_progress | invoice_unpaid (the first invoice is unpaid: an upgrade, or the plan already billed; pay it under Invoices)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/change":{"post":{"summary":"Change plan: Checkout when there is no subscription (the open session again for the same plan; another plan replaces it), immediate upgrade with proration (a declined proration payment answers 402 payment_failed with the card message and changes nothing), downgrade at period end; during a Stripe trial a Starter/Growth switch swaps the plan in place (trial_end and the card untouched, nothing charged; staff only to a cheaper plan: 403 impersonation_no_charge); a trial Stripe has already converted (the org still shows trialing) upgrades only with a preview's proration_date, else 409 change_in_progress, and downgrades at the period end (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["free","starter","growth","scale"]},"proration_date":{"type":"integer","description":"the preview's proration_date; used for an upgrade within the current period, at most 10 min old, else ignored"}},"required":["plan"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"url":{"type":"string","description":"Stripe Checkout"}},"required":["url"]},{"type":"object","properties":{"ok":{"const":true},"plan":{"type":"string","enum":["free","starter","growth","scale"]}},"required":["ok","plan"]},{"type":"object","properties":{"ok":{"const":true},"pending_plan":{"type":"string","enum":["free","starter","growth","scale"]},"effective_at":{"type":"integer","description":"epoch milliseconds"}},"required":["ok","pending_plan","effective_at"]}]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_no_charge (staff: any upgrade; during a Stripe trial, lifting a cancel or schedule; off a trial, a revert that would move Stripe's base price, e.g. taking back a downgrade Stripe has already applied; nothing is changed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ cancel_pending | checkout_completed | billing_busy | change_in_progress | plan_unreadable (a subscription whose plan Billing cannot read, with a downgrade or cancellation pending: taking it back would move the price, so nothing was changed) | trial_not_billable (Stripe is still trialing: overage so far cannot be billed yet, or, re-choosing the current plan, the trial is on another plan or holds a schedule; nothing changed) | preview_expired (the proration_date predates the current period: it renewed since the preview; nothing was changed, preview again) | invoice_unpaid (the first invoice is unpaid: an upgrade, or the plan already billed with nothing pending; nothing changed, pay it under Invoices)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"usage_unavailable: an upgrade (or take-back) could not read the latest usage from edge-analyst, so nothing was changed; retry after Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":0}}}}}}},"/v1/orgs/{org}/billing/cancel":{"post":{"summary":"Cancel at period end; the org becomes Free then (retention drops, enforcement off, email alerts only) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"effective_at":{"type":"integer","description":"epoch milliseconds"}},"required":["ok","effective_at"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/resume":{"post":{"summary":"Undo a pending cancellation (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role; or code ∈ impersonation_no_charge (staff, during a Stripe trial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ schedule_cancel (Stripe still ends it after its subscription schedule's end was released; D1 unchanged, support resolves it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/portal":{"post":{"summary":"Stripe Customer Portal session for payment methods (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/owed":{"get":{"summary":"Open invoices still owed, for any member: number, created (ms), total_usd and amount_due_usd (what is still due: a part-paid or credited invoice owes less than its total) only. Omits id, hosted and PDF links and lines, which stay admin-only on /billing/invoices; empty unless the org is past_due with a Stripe subscription (no Stripe call otherwise)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"number":{"oneOf":[{"type":"string"},{"type":"null"}]},"created":{"type":"integer","description":"epoch milliseconds"},"total_usd":{"type":"number"},"amount_due_usd":{"type":"number"}},"required":["number","created","total_usd","amount_due_usd"]}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/billing/invoices":{"get":{"summary":"Invoices straight from Stripe, hosted and PDF links included; empty when the org has no customer (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/usage":{"get":{"summary":"Events, snapshot polls and ingest bytes per project per UTC day (days of deleted projects included); defaults to the current period","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","description":"YYYY-MM-DD"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","description":"YYYY-MM-DD, inclusive; at most 92 days"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UsageItem"}},"included_events":{"oneOf":[{"type":"integer"},{"type":"null"}]},"period":{"$ref":"#/components/schemas/Period"}},"required":["items","included_events","period"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/usage/sync":{"post":{"summary":"Pull the last three UTC days of every project, and of projects deleted in the last six days (from their delete day), from edge-analyst and report them to the Stripe meter (the daily cron does this at 00:15 UTC) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","description":"day rows written"},"projects":{"type":"integer"},"meter_events_sent":{"type":"integer"},"skipped_reason":{"type":"string"}},"required":["days","projects","meter_events_sent"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/notifications":{"get":{"summary":"In-app notification centre, newest first, with the caller's unread count","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]},{"type":"object","properties":{"unread":{"type":"integer","description":"unread among the caller's newest 100 notifications; 100 means 100 or more"}},"required":["unread"]}]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/notifications/{id}/read":{"post":{"summary":"Mark one notification read for the caller","tags":["projects"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/audit":{"get":{"summary":"Audit log, newest first. Enterprise plan only: other plans get 402 plan_limit (gate audit_log); events are recorded on every plan, so an upgrade shows the full history. Optional filters apply before paging, so every page holds only matching rows: `action` is a comma-separated list of exact action codes (max 100 codes of max 100 chars); `actor` is a case-insensitive substring of the actor email (max 200 chars), and a substring of \"system\" also matches system rows (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"action","in":"query","required":false,"schema":{"type":"string","description":"comma-separated exact action codes, e.g. member.remove,member.role"}},{"name":"actor","in":"query","required":false,"schema":{"type":"string","description":"case-insensitive substring of actor email; \"sys\" also matches system rows","maxLength":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/audit/staff":{"get":{"summary":"camada staff access: impersonation start/end rows and every row written under an impersonation, newest first. Every plan (no audit_log gate); ip and ua are always null (staff network details stay private) (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/projects":{"get":{"summary":"Projects","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Project"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a project: provisions the edge-analyst tenant in learning mode; the key is returned once (admin+)","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"env":{"type":"string","enum":["production","staging","development"]},"learning_days":{"type":"integer","minimum":14,"maximum":90,"default":30}},"required":["name","env"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"camada_key":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["project","camada_key"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ slug_taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}":{"get":{"summary":"Project with tenant state; tenant is null when edge-analyst has no tenant for it (re-provision). Below admin the tenant has no keys and config holds customer settings only","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"tenant":{"oneOf":[{"$ref":"#/components/schemas/TenantInfo"},{"type":"null"}]}},"required":["project","tenant"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Rename and/or update tenant settings (validated by edge-analyst: reject, never clamp); settings need a tenant. Only cadence_minutes, poll_seconds, sample, exclude, trusted_proxy and beacon are settings; any other key (plan, policy, env…) is a 400 (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"settings":{"type":"object","properties":{"cadence_minutes":{"type":"integer","minimum":5,"maximum":360},"poll_seconds":{"type":"integer"},"sample":{"type":"number"},"exclude":{"type":"array","items":{"type":"string"}},"trusted_proxy":{"type":"object","additionalProperties":true},"beacon":{"type":"boolean"}},"additionalProperties":false}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"tenant":{"oneOf":[{"$ref":"#/components/schemas/TenantInfo"},{"type":"null"}]}},"required":["project","tenant"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ tenant_missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete project and purge its tenant; usage reaching the edge until the delete is still counted; body must repeat the project name (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string"}},"required":["confirm"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"usage_unavailable: the project's latest usage could not be read from edge-analyst, so nothing was deleted; retry after Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":0}}}}}}},"/v1/projects/{id}/reprovision":{"post":{"summary":"Re-create the edge-analyst tenant for a project that lost it; the key is returned once (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"tenant":{"$ref":"#/components/schemas/TenantInfo"},"camada_key":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["project","tenant","camada_key"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ tenant_exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/health":{"get":{"summary":"Tenant health + freshness + learning; all null with a tenant_missing action when the tenant is gone","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Health"},{"type":"object","properties":{"freshness":{"oneOf":[{"$ref":"#/components/schemas/Freshness"},{"type":"null"}]},"learning":{"oneOf":[{"$ref":"#/components/schemas/Learning"},{"type":"null"}]},"actions":{"type":"array","items":{"$ref":"#/components/schemas/Action"}}},"required":["freshness","learning","actions"]}]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/setup":{"get":{"summary":"Setup steps derived from data; a missing tenant is the only step until it is re-provisioned","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"steps":{"type":"array","items":{"$ref":"#/components/schemas/SetupStep"}}},"required":["steps"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/overview":{"get":{"summary":"KPIs, action items, recent verdicts, learning progress. The key_rotation_pending action item names the old key fingerprint tail and expiry for admins only; below admin its detail is a plain sentence with neither. The enforcement_off action item (a plan drop left this project on recommend and the plan includes enforcement again) tells a caller below admin to ask an admin","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"kpis":{"type":"object","properties":{"requests":{"type":"integer"},"blocked":{"type":"integer"},"investigated":{"type":"integer"},"active_blocks":{"type":"integer","description":"blocks with action 'block'; challenge rows are counted separately; the system enforcement-check block (203.0.113.66) is not counted"},"challenge_recommended":{"type":"integer"},"recommendations":{"type":"integer"}},"required":["requests","blocked","investigated","active_blocks","challenge_recommended","recommendations"]},"actions":{"type":"array","items":{"$ref":"#/components/schemas/Action"}},"recent_verdicts":{"type":"array","items":{"$ref":"#/components/schemas/Finding"}},"learning":{"oneOf":[{"$ref":"#/components/schemas/Learning"},{"type":"null"}]}},"required":["kpis","actions","recent_verdicts","learning"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/keys":{"get":{"summary":"Key fingerprints and last-used times (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Keys"},{"type":"object","properties":{"urls":{"type":"object","properties":{"ingest":{"oneOf":[{"type":"string"},{"type":"null"}]},"snapshot":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["ingest","snapshot"]}},"required":["urls"]}]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/keys/rotate":{"post":{"summary":"Rotate the key; the old one stays valid for grace_hours (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"grace_hours":{"type":"integer","minimum":1,"maximum":168,"default":24}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"camada_key":{"type":"string"}},"required":["camada_key"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/keys/revoke-previous":{"post":{"summary":"Revoke the previous key now (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/learning":{"patch":{"summary":"Extend, re-enter, enforce, or end learning early (high-confidence only) (admin+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"extend"},"days":{"type":"integer","minimum":1,"maximum":90}},"required":["action","days"]},{"type":"object","properties":{"action":{"const":"reenter"},"days":{"type":"integer","minimum":14,"maximum":90}},"required":["action"]},{"type":"object","properties":{"action":{"type":"string","enum":["enforce","end_early_high_confidence"]}},"required":["action"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"learning":{"$ref":"#/components/schemas/Learning"}},"required":["learning"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the admin role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/rules":{"get":{"summary":"Rule config with catalog and enforceability","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rules"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Update rule config; send any subset of rules/categories/sensitivity, the rest keeps its value (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RulesPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rules"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/rules/custom":{"get":{"summary":"The ordered custom rules, built-ins included; first match wins on both planes","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/CustomRule"}}},"required":["rules"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a custom rule (default at the end of the order) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"enabled":{"type":"boolean"},"action":{"type":"string","enum":["skip","block","challenge","warn"]},"record":{"type":"boolean"},"conds":{"type":"array","items":{"$ref":"#/components/schemas/Condition"},"minItems":1,"maxItems":20},"reason":{"type":"string","maxLength":500},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"place":{"oneOf":[{"const":"first"},{"const":"last"},{"type":"object","properties":{"after":{"type":"string"}},"required":["after"]}],"description":"where in the order it lands; default last"}},"required":["name","action","conds","reason"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rule":{"$ref":"#/components/schemas/CustomRule"}},"required":["rule"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/rules/custom/order":{"put":{"summary":"Reorder the whole list; the body is a permutation of every rule id (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"}}},"required":["order"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/CustomRule"}}},"required":["rules"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/rules/custom/{rule}":{"put":{"summary":"Edit a custom rule; a built-in takes only `enabled` (400 builtin_rule otherwise) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"rule","in":"path","required":true,"schema":{"type":"string","description":"rule id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"enabled":{"type":"boolean"},"action":{"type":"string","enum":["skip","block","challenge","warn"]},"record":{"type":"boolean"},"conds":{"type":"array","items":{"$ref":"#/components/schemas/Condition"},"maxItems":20},"reason":{"type":"string","maxLength":500},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":[],"minProperties":1}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rule":{"$ref":"#/components/schemas/CustomRule"}},"required":["rule"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a custom rule; a built-in is 400 builtin_rule (disable it instead) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"rule","in":"path","required":true,"schema":{"type":"string","description":"rule id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/stack":{"get":{"summary":"What the project runs: user-declared and inferred products, the removed ones, and the vocabulary","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stack"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/stack/{product}":{"put":{"summary":"Declare a product (400 for an id outside the vocabulary) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"product","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{2,24}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stack"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Remove a product; inference cannot bring it back (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"product","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{2,24}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stack"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/allowlist":{"get":{"summary":"Allow-list","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AllowItem"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add an allow-list entry (reason required); replace: true to overwrite an existing one (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","path","country","asn","header"]},"key":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"},"reason":{"type":"string"},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"replace":{"type":"boolean","description":"true replaces an existing active entry's reason and expiry; without it a key that already has one answers 409 already_exists with the existing row in error.item"}},"required":["key_type","key","reason"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"$ref":"#/components/schemas/AllowItem"},"warning":{"type":"string","description":"LISTS-10: 'v3_sdk_enforces_wider_block' when the entry sits inside a wider active block and no v4 SDK has polled yet"}},"required":["item"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ already_exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Remove an allow-list entry (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"key_type","in":"query","required":false,"schema":{"type":"string","enum":["ip","cidr","path","country","asn","header"]}},{"name":"key","in":"query","required":false,"schema":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/blocks":{"get":{"summary":"Active blocks","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BlockItem"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Block manually with a TTL (null = permanent) or an absolute expires (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key_type":{"type":"string","enum":["ip","cidr","path","country","asn","header"]},"key":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"},"reason":{"type":"string"},"ttl_hours":{"oneOf":[{"type":"integer","minimum":1,"maximum":8760},{"type":"null"}]},"expires":{"oneOf":[{"type":"integer","description":"absolute alternative to ttl_hours: epoch ms, in the future, at most 1 year ahead; not together with a non-null ttl_hours"},{"type":"null"}]},"replace":{"type":"boolean","description":"true replaces an existing active entry's reason and expiry; without it a key that already has one answers 409 already_exists with the existing row in error.item"}},"required":["key_type","key","reason"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"$ref":"#/components/schemas/BlockItem"}},"required":["item"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ allowlisted | already_exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Extend or shorten a block (keys in the query, as DELETE) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"key_type","in":"query","required":false,"schema":{"type":"string","enum":["ip","cidr","path","country","asn","header"]}},{"name":"key","in":"query","required":false,"schema":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ttl_hours":{"oneOf":[{"type":"integer","minimum":1,"maximum":8760},{"type":"null"}]}},"required":["ttl_hours"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["ok","expires"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Unblock (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"key_type","in":"query","required":false,"schema":{"type":"string","enum":["ip","cidr","path","country","asn","header"]}},{"name":"key","in":"query","required":false,"schema":{"type":"string","maxLength":566,"description":"the address, CIDR, path (any case; ending in / makes a prefix that also covers the directory itself), ISO alpha-2 country code or decimal ASN — or, for a header entry, \"<name>: <value>\" with the name lower-cased (§A4)"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/alerts/catalog":{"get":{"summary":"Versioned alert event catalog with an example payload each (ALRT-02)","tags":["orgs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"schema_version":{"const":1},"events":{"type":"array","items":{"$ref":"#/components/schemas/AlertCatalogEntry"}}},"required":["schema_version","events"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/channels":{"get":{"summary":"Alert channels, secrets masked","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AlertChannel"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a channel; a webhook secret is generated when absent and returned ONCE (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"type":{"const":"slack"},"name":{"type":"string"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},"cap_per_hour":{"type":"integer"}},"required":["type","name","config"]},{"type":"object","properties":{"type":{"const":"webhook"},"name":{"type":"string"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"secret":{"type":"string"}},"required":["url"]},"cap_per_hour":{"type":"integer"}},"required":["type","name","config"]},{"type":"object","properties":{"type":{"const":"email"},"name":{"type":"string"},"config":{"type":"object","properties":{"emails":{"oneOf":[{"const":"members"},{"type":"array","items":{"type":"string","format":"email"},"description":"addresses of current org members only; any other address is a 400"}]}},"required":["emails"]},"cap_per_hour":{"type":"integer"}},"required":["type","name","config"]}]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"$ref":"#/components/schemas/AlertChannel"},"secret":{"type":"string","description":"webhook only, shown once"}},"required":["channel"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/channels/{cid}":{"patch":{"summary":"Rename, enable/disable (re-enabling clears failing_since), change the cap or the config (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"cap_per_hour":{"type":"integer"},"config":{"type":"object","additionalProperties":true}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"$ref":"#/components/schemas/AlertChannel"}},"required":["channel"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ channel_changed (a concurrent edit or rotate changed the config first; nothing is saved; reload and retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a channel and the routes pointing at it (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/channels/{cid}/rotate-secret":{"post":{"summary":"New webhook signing secret, returned once. The old secret keeps signing for 24 h (the header then carries one v1 per secret, in no promised order; receivers accept if any matches); a second rotate drops it at once (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string"},"previous_secret_expires":{"oneOf":[{"type":"integer","description":"epoch ms until the old secret also signs; null when the channel had no secret"},{"type":"null"}]}},"required":["secret","previous_secret_expires"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ channel_changed (a concurrent edit or rotate won; reload and retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/channels/{cid}/test":{"post":{"summary":"Send the catalog's test payload through the real transport, synchronously; a failing receiver is a 200 with a failed delivery. Throttled per channel: one per 30 s and at most min(cap_per_hour, 5) per hour, else 429 with retry_after (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"delivery":{"$ref":"#/components/schemas/AlertDelivery"}},"required":["delivery"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/routes":{"get":{"summary":"Event to channel routing","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"routes":{"type":"array","items":{"$ref":"#/components/schemas/AlertRoute"}}},"required":["routes"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Replace the whole routing set; an unknown event or channel is a 400 (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"routes":{"type":"array","items":{"$ref":"#/components/schemas/AlertRoute"}}},"required":["routes"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"routes":{"type":"array","items":{"$ref":"#/components/schemas/AlertRoute"}}},"required":["routes"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/deliveries":{"get":{"summary":"Delivery log, newest first","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"channel_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AlertDeliveryStatus"}},{"name":"event","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AlertDelivery"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/alerts/mute":{"get":{"summary":"Mute state; critical alerts are delivered anyway","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"until":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["until"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Mute non-critical alerts until a time (null clears) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"until":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["until"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"until":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["until"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/staff/orgs":{"get":{"summary":"Every organization, for the staff console (SEC-06)","tags":["orgs"],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/StaffOrg"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/staff/impersonate":{"post":{"summary":"Act as an admin of one org for 60 minutes; audited in that org at both ends. 400 staff_org when the target is the staff organization itself (it cannot be impersonated)","tags":["orgs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"org_id":{"type":"string"},"reason":{"type":"string","minLength":10,"maxLength":500}},"required":["org_id","reason"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"impersonation":{"$ref":"#/components/schemas/Impersonation"}},"required":["impersonation"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden; code ∈ forbidden (not camada staff) | two_factor_required (the staff user has not turned on two-factor; no grant, no cookie, no audit row)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ impersonation_active (this staff user already holds a live grant, including when a parallel start claimed it first; no cookie, no audit row; error.org is {id, name} of the live grant's org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"End your live grant — this session's or, if it is not carrying the cookie, whichever one you hold","tags":["orgs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ended":{"type":"integer","description":"1 when a grant was ended, 0 when there was nothing to end"},"org_id":{"type":"string"}},"required":["ok","ended"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/staff/edge-resync":{"post":{"summary":"Deploy-time resync (contract edge-tenant-billing v1): push plan, org and paid to every live tenant of one page of orgs, by org id; idempotent. Limited to one call per 10 seconds per staff user: 429 rate_limited with Retry-After seconds. The limit is per isolate and best effort, and a call that fails gives its slot back. Writes a staff.edge_resync audit row even when the call fails part way (aborted: true)","tags":["orgs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cursor":{"type":"string","description":"next_cursor of the previous page"},"limit":{"type":"integer","minimum":1,"maximum":100}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"orgs":{"type":"integer"},"tenants":{"type":"integer","description":"tenants patched"},"missing":{"type":"integer","description":"projects with no tenant on edge-analyst"},"failed":{"type":"array","items":{"type":"object","properties":{"org_id":{"type":"string"},"code":{"type":"string","description":"stable code, currently edge_unavailable; the upstream detail is logged, never returned"}},"required":["org_id","code"]}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["orgs","tenants","missing","failed","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/staff/orgs/{org}/test-clock":{"post":{"summary":"Stripe test clock for trial UAT (camada-all-s8sz): creates a clock and the org's Stripe customer on it and binds the customer, so the trial Checkout lives on the clock. 404 unless STRIPE_MOCK in a dev env, or the Stripe secret key is a test-mode one (checked before any Stripe call). Audited staff.test_clock.created","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"required":[]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"clock":{"type":"object","properties":{"id":{"type":"string"},"frozen_time":{"type":"integer","description":"epoch milliseconds"},"status":{"type":"string"}},"required":["id","frozen_time","status"]},"customer":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["clock","customer"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ customer_exists (the org already has a Stripe customer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"The org's test clock (null when none) and its billing state, for polling after an advance. 404 as for create","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"clock":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"},"frozen_time":{"type":"integer","description":"epoch milliseconds"},"status":{"type":"string"}},"required":["id","frozen_time","status"]},{"type":"null"}]},"billing":{"type":"object","properties":{"status":{"type":"string"},"plan":{"type":"string","enum":["free","starter","growth","scale"]},"trial_end":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"first_paid_at":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"cancel_at_period_end":{"type":"boolean"}},"required":["status","plan","trial_end","first_paid_at","cancel_at_period_end"]}},"required":["clock","billing"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/staff/orgs/{org}/test-clock/advance":{"post":{"summary":"Advance the org's test clock by whole days (1-30). 404 as for create. Audited staff.test_clock.advanced","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","minimum":1,"maximum":30}},"required":["days"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"clock":{"type":"object","properties":{"id":{"type":"string"},"frozen_time":{"type":"integer","description":"epoch milliseconds"},"status":{"type":"string"}},"required":["id","frozen_time","status"]}},"required":["clock"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ no_test_clock (the org has no clock) | clock_busy (the clock is still advancing)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/chat/workflows":{"get":{"summary":"The guided workflows the chat launcher offers (CHAT-04)","tags":["orgs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"args":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["string","key"]},"required":{"type":"boolean"}},"required":["name","type","required"]}},"result_schema":{"type":"object","additionalProperties":true}},"required":["id","title","description","args","result_schema"]}}},"required":["items"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/chat/conversations":{"get":{"summary":"Your conversations in this project, newest first (private to you)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ChatConversation"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Start a conversation","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120}},"required":[]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversation":{"$ref":"#/components/schemas/ChatConversation"}},"required":["conversation"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/chat/conversations/{cid}":{"get":{"summary":"One conversation with its messages and change cards","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversation":{"$ref":"#/components/schemas/ChatConversation"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}},"proposals":{"type":"array","items":{"$ref":"#/components/schemas/ChatProposal"}}},"required":["conversation","messages","proposals"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Rename a conversation","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120}},"required":["title"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversation":{"$ref":"#/components/schemas/ChatConversation"}},"required":["conversation"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a conversation and its messages","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/chat/conversations/{cid}/messages":{"post":{"summary":"Ask the agent — text/event-stream by default, ?stream=0 for one JSON body","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}},{"name":"stream","in":"query","required":false,"schema":{"type":"string","enum":["0","1"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","maxLength":4000},"workflow":{"type":"string"},"args":{"type":"object","additionalProperties":true}},"required":["content"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"$ref":"#/components/schemas/ChatMessage"},"usage":{"oneOf":[{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"cost_usd":{"type":"number"},"model":{"type":"string"},"fallback":{"type":"boolean"}},"required":["input_tokens","output_tokens","cost_usd","model","fallback"]},{"type":"null"}]},"budget":{"$ref":"#/components/schemas/ChatBudget"},"canned":{"type":"string","description":"'budget_exhausted' when the month's tokens are gone and no model was called"}},"required":["message","budget"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ conversation_busy (a reply is still being written in this conversation; one turn at a time per conversation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/chat/conversations/{cid}/proposals/{pid}/confirm":{"post":{"summary":"Apply a change card under your own role (CHAT-03); the audit row records actor = you, via the agent","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}},{"name":"pid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"proposal":{"$ref":"#/components/schemas/ChatProposal"}},"required":["proposal"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ proposal_not_pending | proposal_expired (drafted more than 24 hours ago)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/chat/conversations/{cid}/proposals/{pid}/reject":{"post":{"summary":"Decline a change card","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"string"}},{"name":"pid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"proposal":{"$ref":"#/components/schemas/ChatProposal"}},"required":["proposal"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict; code ∈ proposal_not_pending | proposal_expired (drafted more than 24 hours ago)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/chat/messages/{mid}/feedback":{"post":{"summary":"Thumbs up or down on an assistant message (CHAT-06)","tags":["orgs"],"parameters":[{"name":"mid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"vote":{"type":"string","enum":["up","down"]},"note":{"type":"string","maxLength":1000}},"required":["vote"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/orgs/{org}/chat/usage":{"get":{"summary":"This month's chat spend and what is left of the plan's allowance","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string","description":"org id or slug"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"$ref":"#/components/schemas/Period"},"messages":{"type":"integer"},"tokens_in":{"type":"integer"},"tokens_out":{"type":"integer"},"cost_usd":{"type":"number"},"budget":{"$ref":"#/components/schemas/ChatBudget"}},"required":["period","messages","tokens_in","tokens_out","cost_usd","budget"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/memory/facts":{"get":{"summary":"Memory facts, newest first (retired ones are never listed)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"kind","in":"query","required":false,"schema":{"$ref":"#/components/schemas/FactKind"}},{"name":"source","in":"query","required":false,"schema":{"$ref":"#/components/schemas/FactSource"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MemoryFact"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Remember something about this customer (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/FactKind"},"text":{"type":"string","minLength":1,"maxLength":500},"entity":{"oneOf":[{"$ref":"#/components/schemas/FactEntity"},{"type":"null"}]},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":["kind","text"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"fact":{"$ref":"#/components/schemas/MemoryFact"}},"required":["fact"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/memory/facts/{fid}":{"patch":{"summary":"Edit a fact (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"fid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/FactKind"},"text":{"type":"string","minLength":1,"maxLength":500},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]}},"required":[],"minProperties":1}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"fact":{"$ref":"#/components/schemas/MemoryFact"}},"required":["fact"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Forget a fact, and its vector with it (MEM-04 undo) (member+)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"fid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Requires the member role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/memory/search":{"get":{"summary":"Semantic search over this project's memory only","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":500}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":20,"default":5}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"doc":{"$ref":"#/components/schemas/MemoryDoc"},"score":{"type":"number"}},"required":["doc","score"]}}},"required":["items"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/memory/export":{"get":{"summary":"Everything this project remembers, as a JSON download","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":"string"},"exported_at":{"type":"integer","description":"epoch milliseconds"},"facts":{"type":"array","items":{"$ref":"#/components/schemas/MemoryFact"}},"docs":{"type":"array","items":{"$ref":"#/components/schemas/MemoryDoc"}}},"required":["project_id","exported_at","facts","docs"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/findings/entities":{"get":{"summary":"Activity list: one row per visitor (sessions folded under their IP), paged on the server with a total","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"verdict","in":"query","required":false,"schema":{"type":"string","enum":["malicious","suspicious","benign"]}},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"action","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"tap","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"a tap from /findings/facets"}},{"name":"key","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"exact entity key"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"prefix search on IP / session id"}},{"name":"key_type","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["ts","confidence","score"]}},{"name":"dir","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"description":"default 50; the UI offers 25, 50, 100"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EntityRow"}},"total":{"type":"integer"},"page":{"type":"integer"},"page_size":{"type":"integer"}},"required":["items","total","page","page_size"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/findings/entities/counts":{"get":{"summary":"The Activity tiles in one call: visitors in the range, and how many of them show Malicious / Would block / Would challenge (browser page loads only; API calls cannot be challenged) / Blocked. Each equals findings/entities' total under that filter","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"flagged":{"type":"integer"},"malicious":{"type":"integer"},"recommended":{"type":"integer"},"challenge_recommended":{"type":"integer"},"blocked":{"type":"integer"}},"required":["flagged","malicious","recommended","challenge_recommended","blocked"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/findings/facets":{"get":{"summary":"Options for the findings filters that depend on the project's data","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"taps":{"type":"array","items":{"type":"string"}}},"required":["taps"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/findings":{"get":{"summary":"Findings (verdicts, blocks, recommendations), newest first","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"verdict","in":"query","required":false,"schema":{"type":"string","enum":["malicious","suspicious","benign"]}},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"action","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"tap","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"a tap from /findings/facets"}},{"name":"key","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"exact entity key"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"prefix search on IP / session id"}},{"name":"key_type","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["ts","confidence","score"]}},{"name":"dir","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]}},{"name":"session_ip","in":"query","required":false,"schema":{"type":"string","description":"sessions filed under this IP (key_type=session&session_ip=<ip>)"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"alerted","in":"query","required":false,"schema":{"type":"string","description":"any | none | <channel_id>; filtered over the page, so a page can be short and an empty one is not the end"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Finding"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/entities/{key_type}/{key}":{"get":{"summary":"Entity detail","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"key_type","in":"path","required":true,"schema":{"type":"string","enum":["ip","cidr","session","tlsx","asn","path","cfp"],"description":"what the key is; cfp is a client fingerprint (read-only: never allow-listed or blocked); a path finding is a one-try-per-IP farm alert (never blocked; key is a path or '*')"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"state":{"type":"string","enum":["blocked","recommended","challenge_recommended","allowed","cleared","ignored","warned","grey","unknown"],"description":"the entity's standing, in detail's vocabulary: the Activity row's outcome (manual_block reads 'blocked'; 'allowed' on a benign or no-action verdict with no allow entry reads 'cleared'); with no verdict, 'allowed' (an allow entry, or a skip rule's greylist 'allowed' stamp while the Allow-list switch is on; that stamp also keeps a verdict-'allowed' from reading 'cleared'), 'challenge_recommended' or 'blocked' (a block row), else the greylist word ('grey', also standing in for a stale or paused allowed/blocked; 'recommended', 'cleared', 'ignored', 'warned') or 'unknown' when there is no greylist row"},"ip_count":{"oneOf":[{"type":"integer","minimum":0,"maximum":500},{"type":"null"}],"description":"for key_type 'cfp', the distinct contributing IPs (IPv6 collapsed to /64); 500 is the cap and means 500 or more; null for every other key type or when unknown"},"allow_inactive":{"type":"boolean","description":"true only while the built-in Allow-list switch is off and an allow entry exists for this entity: state is unchanged, the entry is paused"},"outcome":{"oneOf":[{"type":"string","enum":["blocked","recommended","challenge_recommended","allowed","manual_block","ignored","warned"]},{"type":"null"}],"description":"what the edge did with this entity (the Finding.action list), or null"},"features":{"type":"object","additionalProperties":true,"description":"analyst features passed through; a path farm entity carries farm: { login|pay|coupon|signup: { n, all, share, base, bar, mode, floor, fires, arm } }; an ip entity may carry cves: [{ cve, kev, ransomware, product, source }] (up to 5; KEV = actively exploited per CISA)"},"blocks":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"covering":{"type":"boolean","description":"the governing covering cidr row of an ip, or a single-IP session's IP"},"active":{"type":"boolean"}},"required":["active"]}},"sessions":{"type":"array","description":"an IP's single-IP sessions, [] for other keys; passed through from the edge","items":{"type":"object","additionalProperties":true,"properties":{"key":{"type":"string"},"block":{"description":"the block on this session, or null","oneOf":[{"type":"object","properties":{"action":{"type":"string"},"source":{"type":"string"},"expires":{"oneOf":[{"type":"integer","description":"epoch milliseconds"},{"type":"null"}]},"active":{"type":"boolean"}},"required":["action","source","expires","active"]},{"type":"null"}]}}}}}}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/entities/{key_type}/{key}/requests":{"get":{"summary":"Every request an ip, session or IPv6 /64 (cidr) made (oldest first; a /64 row carries the requesting ip), for the dossier timeline. Raw events live 7 days; from and to must be valid dates at most 7 days apart (400 otherwise)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"key_type","in":"path","required":true,"schema":{"type":"string","enum":["ip","session","cidr"]}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds","minimum":0,"maximum":8640000000000000}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds","minimum":0,"maximum":8640000000000000}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":5000,"default":2000}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EntityRequest"}},"uas":{"type":"array","items":{"type":"string"}},"verdicts":{"type":"array","items":{"type":"object","properties":{"ts":{"type":"integer","description":"epoch milliseconds"},"verdict":{"type":"string"},"category":{"oneOf":[{"type":"string"},{"type":"null"}]},"confidence":{"type":"number"},"action":{"type":"string"},"inconclusive":{"type":"boolean","description":"the analyst's stand-in verdict (Suspicious 50%, no conclusion), not a judgement; absent from older analysts"}},"required":["ts","verdict","category","confidence","action"]}},"window":{"type":"object","properties":{"from":{"type":"integer","description":"epoch milliseconds"},"to":{"type":"integer","description":"epoch milliseconds"},"retention_floor":{"type":"integer","description":"epoch milliseconds"},"clamped":{"type":"boolean","description":"from/to were narrowed to retention and/or the newest 24 h; from <= to always"}},"required":["from","to","retention_floor","clamped"]},"truncated":{"type":"boolean","description":"more than `limit` matched: items are the NEWEST `limit` rows and window.from starts at the first of them"},"total":{"type":"integer","description":"how many requests matched in the window (>= items.length)"},"outside_retention":{"type":"boolean"},"duration_available":{"type":"boolean","description":"false when no returned row has dur (logpush / beacon / older SDKs)"}},"required":["items","uas","verdicts","window","truncated","total","outside_retention","duration_available"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/runs":{"get":{"summary":"Analysis runs","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Run"}},"next_cursor":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["items","next_cursor"]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/runs/{run}":{"get":{"summary":"One run with its markdown report","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"run","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Run"},{"type":"object","properties":{"report":{"oneOf":[{"type":"string","description":"null until the run finishes, and for a failed run"},{"type":"null"}]}},"required":["report"]}]}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/rollups":{"get":{"summary":"Hourly rollups","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Rollup"}}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/events":{"get":{"summary":"Raw events explorer (edge-analyst passthrough)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"minutes","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":10080}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","description":"epoch milliseconds"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/projects/{id}/usage":{"get":{"summary":"Usage by route (edge-analyst passthrough)","tags":["projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"project id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed, or edge-analyst rejected the change (edge_rejected)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid session (code `unauthenticated`): no cookie, a bad signature, or an expired or signed-out session. Sign in again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not a member of the org (cross-tenant access is indistinguishable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}