{"openapi":"3.1.0","info":{"title":"Kaia API","version":"2026-07","description":"The published Kaia platform API. Authenticate with a per-organization API key: `Authorization: Bearer kaia_live_…` (self-serve: Settings → Integrations → Developer access). Scopes: `intake:write` (submit/confirm), `intake:read` (document/batch status), `runs:read` (READ a run and what the engine decided — strictly read-only; there is no runs:write). Matter targeting is ALWAYS explicit (matterId XOR newMatter); batches resolve their matter once and members inherit it. Two ways to learn what happened: POLL the run/result endpoints, or subscribe to signed WEBHOOKS (see the `webhooks` section — HMAC-SHA256, timestamped, replay-windowed, at-least-once with bounded retry)."},"servers":[{"url":"https://portal.kaiaai.ai"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Per-org API key secret, shape kaia_live_<32B-urlsafe>. Shown once at issuance; only its SHA-256 digest is stored."}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem document. `code` is the stable machine identifier: invalid_request · unauthorized · forbidden_scope · matter_targeting_required · matter_not_found · matter_closed · payload_too_large · unsupported_media_type · rate_limited · processing_unavailable · not_found · internal_error · unsupported_vertical.","required":["title","status","code","detail"],"properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","enum":["invalid_request","unauthorized","forbidden_scope","matter_targeting_required","matter_not_found","matter_closed","payload_too_large","unsupported_media_type","rate_limited","processing_unavailable","not_found","internal_error","unsupported_vertical"]},"detail":{"type":"string"}}},"NewMatter":{"type":"object","additionalProperties":false,"properties":{"reference":{"type":"string","maxLength":200,"description":"Human business id, org-unique per vertical."},"title":{"type":"string","maxLength":300}}},"MatterBinding":{"type":"object","required":["id","reference","minted"],"properties":{"id":{"type":"string"},"reference":{"type":"string"},"minted":{"type":"boolean","description":"true when this call created the matter."}}},"RunSummary":{"type":"object","description":"A run is the unit of ORCHESTRATION — owned by a matter, one per role.","required":["runId","verticalId","workflowKey","state","mode","live"],"properties":{"runId":{"type":"string"},"verticalId":{"type":"string"},"workflowKey":{"type":"string"},"matterId":{"type":["string","null"]},"state":{"type":"string","enum":["PENDING","RUNNING","AWAITING_HUMAN","ADVANCED","COMPLETED","CANCELED","FAILED"]},"mode":{"type":"string","enum":["step","auto"],"description":"The two agent modes: step-by-step pauses at every stage gate for human sign-off; auto-pilot advances on its own and stops where a floor or a decision needs a person. Read-only over the API."},"live":{"type":"boolean","description":"RUNNING | AWAITING_HUMAN | ADVANCED."},"awaitingHuman":{"type":"boolean"},"regulatedHold":{"type":"boolean","description":"The compliance FORCE-STOP: the live decision tripped the vertical's regulated floor (e.g. a privileged/attorney-only document class) and is parked for a credentialed human — in both modes. Never auto-produced."},"currentStageId":{"type":["string","null"]},"currentRoleId":{"type":["string","null"]},"startedAt":{"type":"string","format":"date-time"},"lastTransitionAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"}}},"RunResult":{"type":"object","required":["runId","state","floors","receipts","trace"],"properties":{"runId":{"type":"string"},"verticalId":{"type":"string"},"matterId":{"type":["string","null"]},"state":{"type":"string","enum":["PENDING","RUNNING","AWAITING_HUMAN","ADVANCED","COMPLETED","CANCELED","FAILED"]},"currentStageId":{"type":["string","null"]},"currentRoleId":{"type":["string","null"]},"awaitingHuman":{"type":"boolean"},"pendingQuestion":{"type":["object","null"],"description":"Present when the agent has asked a question and the run is AWAITING_HUMAN. Answering is a PORTAL action — the API is read-only on runs.","properties":{"question":{"type":["string","null"]},"header":{"type":["string","null"]},"options":{"type":"array","items":{"type":"object"}}}},"decision":{"type":["object","null"],"description":"Projection of the 12-element Decision Packet the engine persisted for this run. Null before the first agent turn. Elements that the engine did not populate project to null/[] — never to a fabricated default.","properties":{"stage":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}}},"workItem":{"type":["object","null"],"description":"The ONE work item the current turn grounded on — a run carries a single authoritative active work-item identity, and every surface binds to it.","properties":{"id":{"type":"string"},"title":{"type":["string","null"]}}},"finding":{"type":["string","null"]},"recommendation":{"type":["string","null"]},"confidence":{"type":["number","null"],"minimum":0,"maximum":1},"caseFacts":{"type":"array","items":{"type":"string"}},"signalsFound":{"type":"array","items":{"type":"string"}},"evidenceUsed":{"type":"array","items":{"type":"string"}},"gapsOrBlockers":{"type":"array","items":{"type":"string"}},"validRoleAction":{"type":["object","null"],"properties":{"actionId":{"type":"string"},"label":{"type":["string","null"]},"permitted":{"type":"boolean"}}},"downstreamImpact":{"type":["string","null"]},"correctionLearningImpact":{"type":["string","null"]},"benchmarkImpact":{"type":["string","null"]},"matterId":{"type":["string","null"]}}},"classification":{"type":["object","null"],"description":"The grounded document's classification. Multi-axis verticals carry `classificationAxes` as the canonical result and leave the flat `classification` enum null; flat verticals do the reverse. Both are surfaced exactly as stored — the API never flattens one into the other.","properties":{"documentId":{"type":"string"},"title":{"type":"string"},"status":{"type":"string"},"workflowStage":{"type":["string","null"]},"classification":{"type":["string","null"]},"classificationAxes":{"type":["object","null"]},"confidence":{"type":["number","null"]},"modelId":{"type":["string","null"]}}},"floors":{"type":"object","description":"The regulated floors on this run — the compliance hard stops.","required":["regulatedHold","signature"],"properties":{"regulatedHold":{"type":"boolean","description":"Regulated-floor force-stop (e.g. the privileged/attorney-only park) — held for a credentialed human."},"signature":{"type":"object","description":"Signature-gated floor at the current stage: a recorded, credentialed signature is the only release. `requiredCredentials` empty ⇒ this stage is not a signature floor.","required":["requiredCredentials","signedRoleIds","released","held"],"properties":{"stageId":{"type":["string","null"]},"requiredCredentials":{"type":"array","description":"The manifest-declared credentials this floor requires — {roleId, label}, where the label carries the regulatory citation (e.g. \"Qualified Reserves Evaluator · Reg S-K 1202(a)(7)\").","items":{"type":"object","required":["roleId","label"],"properties":{"roleId":{"type":"string"},"label":{"type":"string"}}}},"signedRoleIds":{"type":"array","items":{"type":"string"}},"released":{"type":"boolean"},"held":{"type":"boolean","description":"required non-empty AND not released."}}}}},"receipts":{"type":"array","description":"Per-figure numeric receipts — the computation evidence behind every figure the agent stated (a stated figure is always a computed one). Empty when the run computed no figures; a receipt is never synthesised.","items":{"type":"object"}},"trace":{"type":"array","description":"The most recent 50 state transitions, newest first.","items":{"type":"object","required":["fromState","toState","reason","at"],"properties":{"fromState":{"type":"string","enum":["PENDING","RUNNING","AWAITING_HUMAN","ADVANCED","COMPLETED","CANCELED","FAILED"]},"toState":{"type":"string","enum":["PENDING","RUNNING","AWAITING_HUMAN","ADVANCED","COMPLETED","CANCELED","FAILED"]},"fromStageId":{"type":["string","null"]},"toStageId":{"type":["string","null"]},"reason":{"type":"string"},"at":{"type":"string","format":"date-time"}}}}}},"WebhookEvent":{"type":"object","required":["id","type","createdAt","organizationId","data"],"properties":{"id":{"type":"string","description":"evt_<uuid> — STABLE across retries of the same event. Delivery is AT-LEAST-ONCE: use this as your idempotency key."},"type":{"type":"string","enum":["document.classified","document.failed","batch.completed","session.stage_advanced","session.awaiting_human","session.completed","correction.applied"]},"createdAt":{"type":"string","format":"date-time"},"organizationId":{"type":"string"},"data":{"type":"object","description":"Event-specific identifiers and state. Webhook payloads deliberately carry IDs and state, NOT decision content — read /api/v1/runs/{runId}/result under your key for the decision, so the substance never leaves the key perimeter."}}},"OgErpCostDocument":{"type":"object","description":"Cost and downtime actuals for one well or asset over one period — capital expenditure, operating expenditure and downtime hours, with the reporting currency. This schema is the published contract for structured reserves-data submissions. Submit it as an application/json document through the standard document intake; a document that declares this dataClass should conform to this schema. The intake door accepts the document as JSON and does not reject a nonconforming payload at submission time in this version — conformance is the integration contract, and conformance checking tightens in later versions.","required":["dataClass","provenance","payload"],"additionalProperties":false,"properties":{"dataClass":{"type":"string","enum":["erp-cost"]},"provenance":{"type":"object","description":"Ingestion provenance carried by every data class: the source system that produced the record and the as-of date of the data. Reserves figures are traceable to their source data, so provenance is a required part of every class.","required":["sourceSystem","asOf"],"additionalProperties":false,"properties":{"sourceSystem":{"type":"string","maxLength":200,"description":"The system of origin, named by the submitter (e.g. an ERP, a subsurface data platform, a process historian). Vendor-neutral: any system name is valid."},"asOf":{"type":"string","format":"date","description":"ISO 8601 calendar date (YYYY-MM-DD) the data is current as of."}}},"payload":{"type":"object","required":["wellOrAssetId","period","capex","opex","downtimeHours","currency"],"additionalProperties":false,"properties":{"wellOrAssetId":{"type":"string","maxLength":200},"period":{"type":"string","maxLength":64,"description":"The reporting period the figures cover (e.g. 2026-07 or FY2026)."},"capex":{"type":"number","description":"Capital expenditure for the period."},"opex":{"type":"number","description":"Operating expenditure for the period."},"downtimeHours":{"type":"number","minimum":0},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 currency code (e.g. USD)."}}}}},"OgSubsurfaceDeclineDocument":{"type":"object","description":"Decline data for one producing well: an observed production-rate-vs-time series, fitted Arps decline parameters, or both — plus cumulative production to date and the estimated ultimate recovery, with the rate units stated. At least one of series or arps must be present. This schema is the published contract for structured reserves-data submissions. Submit it as an application/json document through the standard document intake; a document that declares this dataClass should conform to this schema. The intake door accepts the document as JSON and does not reject a nonconforming payload at submission time in this version — conformance is the integration contract, and conformance checking tightens in later versions.","required":["dataClass","provenance","payload"],"additionalProperties":false,"properties":{"dataClass":{"type":"string","enum":["subsurface-decline"]},"provenance":{"type":"object","description":"Ingestion provenance carried by every data class: the source system that produced the record and the as-of date of the data. Reserves figures are traceable to their source data, so provenance is a required part of every class.","required":["sourceSystem","asOf"],"additionalProperties":false,"properties":{"sourceSystem":{"type":"string","maxLength":200,"description":"The system of origin, named by the submitter (e.g. an ERP, a subsurface data platform, a process historian). Vendor-neutral: any system name is valid."},"asOf":{"type":"string","format":"date","description":"ISO 8601 calendar date (YYYY-MM-DD) the data is current as of."}}},"payload":{"type":"object","required":["wellId","cumulativeToDate","eur","units"],"additionalProperties":false,"properties":{"wellId":{"type":"string","maxLength":200},"series":{"type":"array","minItems":1,"description":"Observed production-rate-vs-time points.","items":{"type":"object","required":["t","rate"],"additionalProperties":false,"properties":{"t":{"type":"string","description":"ISO 8601 date or date-time of the observation."},"rate":{"type":"number","minimum":0}}}},"arps":{"type":"object","description":"Fitted Arps decline parameters.","required":["qi","di","b","qe"],"additionalProperties":false,"properties":{"qi":{"type":"number","description":"Initial production rate."},"di":{"type":"number","description":"Initial decline rate."},"b":{"type":"number","minimum":0,"description":"Arps decline exponent. Conventional drive mechanisms fall in the physical 0-1 range; values above 1 are legitimate for unconventional (tight/shale) transient flow and should carry a reservoir justification. Out-of-range values are isolated for engineering review downstream, not refused at submission."},"qe":{"type":"number","description":"Production rate at the economic limit."}}},"cumulativeToDate":{"type":"number","minimum":0},"eur":{"type":"number","minimum":0,"description":"Estimated ultimate recovery."},"units":{"type":"string","maxLength":64,"description":"Rate/volume units for the figures (e.g. bbl/d, Mscf/d)."}}}}},"OgTelemetryDocument":{"type":"object","description":"A stream of observed operational telemetry points for one well — pressure, flow, flare or downtime — each point a timestamp and a value, with the units stated. This schema is the published contract for structured reserves-data submissions. Submit it as an application/json document through the standard document intake; a document that declares this dataClass should conform to this schema. The intake door accepts the document as JSON and does not reject a nonconforming payload at submission time in this version — conformance is the integration contract, and conformance checking tightens in later versions.","required":["dataClass","provenance","payload"],"additionalProperties":false,"properties":{"dataClass":{"type":"string","enum":["telemetry"]},"provenance":{"type":"object","description":"Ingestion provenance carried by every data class: the source system that produced the record and the as-of date of the data. Reserves figures are traceable to their source data, so provenance is a required part of every class.","required":["sourceSystem","asOf"],"additionalProperties":false,"properties":{"sourceSystem":{"type":"string","maxLength":200,"description":"The system of origin, named by the submitter (e.g. an ERP, a subsurface data platform, a process historian). Vendor-neutral: any system name is valid."},"asOf":{"type":"string","format":"date","description":"ISO 8601 calendar date (YYYY-MM-DD) the data is current as of."}}},"payload":{"type":"object","required":["wellId","stream","points","units"],"additionalProperties":false,"properties":{"wellId":{"type":"string","maxLength":200},"stream":{"type":"string","enum":["pressure","flow","flare","downtime"]},"points":{"type":"array","minItems":1,"items":{"type":"object","required":["t","value"],"additionalProperties":false,"properties":{"t":{"type":"string","description":"ISO 8601 date or date-time of the observation."},"value":{"type":"number"}}}},"units":{"type":"string","maxLength":64}}}}},"OgOperatingPlansDocument":{"type":"object","description":"Planned activity and volumes over a stated horizon — a plan identifier, the horizon it covers, and per-period entries. Defined in this version but not yet exercised by any flow: entries carry a period plus free-form plan fields until the class is exercised and its entry shape hardens. This schema is the published contract for structured reserves-data submissions. Submit it as an application/json document through the standard document intake; a document that declares this dataClass should conform to this schema. The intake door accepts the document as JSON and does not reject a nonconforming payload at submission time in this version — conformance is the integration contract, and conformance checking tightens in later versions.","required":["dataClass","provenance","payload"],"additionalProperties":false,"properties":{"dataClass":{"type":"string","enum":["operating-plans"]},"provenance":{"type":"object","description":"Ingestion provenance carried by every data class: the source system that produced the record and the as-of date of the data. Reserves figures are traceable to their source data, so provenance is a required part of every class.","required":["sourceSystem","asOf"],"additionalProperties":false,"properties":{"sourceSystem":{"type":"string","maxLength":200,"description":"The system of origin, named by the submitter (e.g. an ERP, a subsurface data platform, a process historian). Vendor-neutral: any system name is valid."},"asOf":{"type":"string","format":"date","description":"ISO 8601 calendar date (YYYY-MM-DD) the data is current as of."}}},"payload":{"type":"object","required":["planId","horizon","entries"],"additionalProperties":false,"properties":{"planId":{"type":"string","maxLength":200},"horizon":{"type":"string","maxLength":200,"description":"The horizon the plan covers (e.g. FY2027, 2026-2030)."},"entries":{"type":"array","minItems":1,"items":{"type":"object","required":["period"],"properties":{"period":{"type":"string","maxLength":64}}}}}}}}}},"webhooks":{"document.classified":{"post":{"operationId":"onDocumentClassified","summary":"A submitted document finished classification — carries the classification (flat or multi-axis, exactly as stored), the confidence and whether it fell below the vertical threshold.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"document.failed":{"post":{"operationId":"onDocumentFailed","summary":"A submitted document failed processing — carries the classified failure kind and message.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"batch.completed":{"post":{"operationId":"onBatchCompleted","summary":"Every member of a batch has settled (processed + failed = total) — the overnight-batch client’s completion signal.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"session.stage_advanced":{"post":{"operationId":"onSessionStageAdvanced","summary":"A run advanced to a new stage of the regulated process.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"session.awaiting_human":{"post":{"operationId":"onSessionAwaitingHuman","summary":"A run is parked for a human — the agent asked a question, or a role review/regulated floor holds. Carries the FACT, not the question text.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"session.completed":{"post":{"operationId":"onSessionCompleted","summary":"A run reached its terminal state.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}},"correction.applied":{"post":{"operationId":"onCorrectionApplied","summary":"A human correction was persisted and routed into the Intelligence Engine — carries the correction id, type and assigned learning layer.","description":"VERIFY EVERY DELIVERY. Headers:\n  Kaia-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>\n  Kaia-Event: <event type>   Kaia-Delivery: evt_<uuid>   Kaia-Webhook-Id: whep_<uuid>\n\nThe signed material is `${t}.${rawBody}` — the timestamp is INSIDE the mac, so a captured payload cannot be re-dated. Verify by: (1) recomputing HMAC-SHA256 over `${t}.${rawBody}` with your endpoint secret and comparing in CONSTANT TIME; (2) rejecting any delivery whose `t` is more than 300 seconds from your clock (the replay window).\n\nDELIVERY: at-least-once, 3 attempts, 1s → 5s backoff, 5s per-request timeout. Retried on network error, timeout, 429 and 5xx; ANY other 4xx dead-letters immediately (a misconfigured receiver is not retried). Respond 2xx fast — do the work asynchronously. Failed and delivered attempts alike are recorded and readable in Settings → Integrations → Developer access.\n\nThe endpoint secret is shown ONCE at registration and once at rotation. It is not retrievable afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged. Any 2xx ends the delivery. A non-2xx is retried (429/5xx) or dead-lettered (other 4xx)."}}}}},"paths":{"/api/v1/documents":{"post":{"operationId":"submitDocument","summary":"Create a document and obtain a presigned S3 upload URL","description":"Presign flow (D2 — presign-only in v1): create the document, PUT the bytes to `uploadUrl` (expires in 15 minutes), then POST /confirm. Targeting: matterId XOR newMatter; when batchId is present the document INHERITS the batch matter and any member-level matter field is a 400. Limits: 50MB per file; the accepted `mimeType` values are exactly the enum on this request body.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["verticalId","filename","mimeType","sizeBytes"],"properties":{"verticalId":{"type":"string","enum":["legal-ediscovery","accounts-payable","finance-claims","oil-gas-ecr"],"description":"Verticals available on the public API today. Any other value is refused with code unsupported_vertical. For accounts-payable, submissions are invoices only — purchase orders and goods receipts arrive through your ERP connector setup, never through this endpoint."},"filename":{"type":"string"},"mimeType":{"type":"string","enum":["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","text/plain","application/json","text/csv","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","message/rfc822","application/vnd.ms-outlook","application/x-pst","application/zip","application/x-zip-compressed","application/octet-stream","image/jpeg","image/jpg","image/png","image/gif","image/webp","image/tiff","image/bmp"]},"sizeBytes":{"type":"integer","minimum":1,"maximum":52428800},"title":{"type":"string","maxLength":300},"batchId":{"type":"string"},"matterId":{"type":"string"},"newMatter":{"$ref":"#/components/schemas/NewMatter"}}}}}},"responses":{"201":{"description":"Document created; PUT the file bytes to uploadUrl before it expires.","content":{"application/json":{"schema":{"type":"object","required":["documentId","uploadUrl","expiresAt","matter"],"properties":{"documentId":{"type":"string"},"uploadUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"},"matter":{"$ref":"#/components/schemas/MatterBinding"}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found (batch) | matter_not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"matter_closed | duplicate matter reference","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"payload_too_large (50MB limit)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"unsupported_media_type","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listDocuments","summary":"List documents in a matter (cursor-paged)","parameters":[{"name":"matterId","in":"query","required":true,"schema":{"type":"string"},"description":"The matter whose documents to list. Documents are never listed across matters."},{"name":"verticalId","in":"query","schema":{"type":"string"}},{"name":"batchId","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["DISCOVERED","PRESERVED","COLLECTED","PROCESSED","AI_REVIEWED","HUMAN_VERIFIED","ANALYZED","PRODUCED","PRESENTED"]}},{"name":"classification","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of documents plus the cursor for the next page.","content":{"application/json":{"schema":{"type":"object","required":["documents"],"properties":{"documents":{"type":"array","items":{"type":"object"}},"nextCursor":{"type":"string"}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/documents/{documentId}":{"get":{"operationId":"getDocumentStatus","summary":"Document processing status (the polling contract)","parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Processing state snapshot.","content":{"application/json":{"schema":{"type":"object","required":["documentId","status","processingJob"],"properties":{"documentId":{"type":"string"},"status":{"type":"string","enum":["DISCOVERED","PRESERVED","COLLECTED","PROCESSED","AI_REVIEWED","HUMAN_VERIFIED","ANALYZED","PRODUCED","PRESENTED"]},"workflowStage":{"type":["string","null"]},"classification":{"type":["string","null"]},"classificationConfidence":{"type":["number","null"]},"processingJob":{"type":"object","description":"Durable job snapshot: {state, startedAt, finishedAt, …}."}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/documents/{documentId}/confirm":{"post":{"operationId":"confirmDocument","summary":"Confirm the S3 upload; processing chains automatically (D1)","description":"Call after the PUT to uploadUrl succeeds. The server chains classification processing itself (SQS when configured, sync fallback) — external clients never orchestrate engine internals. Idempotent: re-confirming a document already in processing returns 202.","parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Accepted — poll GET /api/v1/documents/{documentId} for terminal state.","content":{"application/json":{"schema":{"type":"object","required":["documentId","status"],"properties":{"documentId":{"type":"string"},"status":{"type":"string","enum":["processing"]}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"processing_unavailable — queue not accepting; document stays in intake for retry","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/batches":{"post":{"operationId":"createBatch","summary":"Create an ingestion batch (resolves the matter once)","description":"PDO's overnight pattern: the batch resolves its matter at creation (matterId XOR newMatter, required); member documents carry batchId and inherit the batch's single case/matter target — there is no per-member override. Counters increment on process/fail. What a batch means per vertical: for legal-ediscovery, a batch groups documents under one matter and never changes how each document is processed; for finance-claims, a batch is adjudicated as one governed run with per-claim results; for accounts-payable, a batch is an invoice load worked as one payment cycle; for oil-gas-ecr, a batch submits many items under one filing target, each processed as its own case.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["verticalId","name","totalDocuments"],"properties":{"verticalId":{"type":"string","enum":["legal-ediscovery","accounts-payable","finance-claims","oil-gas-ecr"],"description":"Verticals available on the public API today. Any other value is refused with code unsupported_vertical. For accounts-payable, submissions are invoices only — purchase orders and goods receipts arrive through your ERP connector setup, never through this endpoint."},"name":{"type":"string","maxLength":200},"totalDocuments":{"type":"integer","minimum":1},"matterId":{"type":"string"},"newMatter":{"$ref":"#/components/schemas/NewMatter"}}}}}},"responses":{"201":{"description":"Batch created with its resolved matter binding.","content":{"application/json":{"schema":{"type":"object","required":["batchId","name","totalDocuments","status","matter"],"properties":{"batchId":{"type":"string"},"name":{"type":"string"},"totalDocuments":{"type":"integer"},"status":{"type":"string"},"matter":{"$ref":"#/components/schemas/MatterBinding"}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"matter_not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"matter_closed | duplicate matter reference","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/batches/{batchId}":{"get":{"operationId":"getBatchStatus","summary":"Batch progress","parameters":[{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Batch counters and progress.","content":{"application/json":{"schema":{"type":"object","required":["batchId","totalDocuments","processed","failed","remaining","status","progress"],"properties":{"batchId":{"type":"string"},"name":{"type":"string"},"totalDocuments":{"type":"integer"},"processed":{"type":"integer"},"failed":{"type":"integer"},"remaining":{"type":"integer"},"status":{"type":"string"},"progress":{"type":"integer","description":"Percent complete, 0–100."}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/runs":{"get":{"operationId":"listRuns","summary":"List runs (cursor-paged)","description":"A run is the unit of orchestration — owned by a matter, one per role. Scope `runs:read`. Filter by vertical, matter, an exact state, or `live=true` (RUNNING | AWAITING_HUMAN | ADVANCED). Newest transition first.","parameters":[{"name":"verticalId","in":"query","schema":{"type":"string"}},{"name":"matterId","in":"query","schema":{"type":"string"}},{"name":"state","in":"query","schema":{"type":"string","enum":["PENDING","RUNNING","AWAITING_HUMAN","ADVANCED","COMPLETED","CANCELED","FAILED"]}},{"name":"live","in":"query","schema":{"type":"boolean"},"description":"Mutually exclusive with `state` (400 if both are given)."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of runs plus the cursor for the next page.","content":{"application/json":{"schema":{"type":"object","required":["runs"],"properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/RunSummary"}},"nextCursor":{"type":"string"}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/runs/{runId}":{"get":{"operationId":"getRun","summary":"Run state (the cheap poll)","description":"State, stage, role, mode, awaiting-human and the regulated-floor force-stop, plus the matter binding and any pending question. Scope `runs:read`. A run belonging to another organization returns 404, never 403 — no existence oracle.","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Run state snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RunSummary"},{"type":"object","properties":{"matter":{"type":["object","null"],"properties":{"id":{"type":"string"},"reference":{"type":"string"},"title":{"type":"string"},"status":{"type":"string"}}},"pendingQuestion":{"type":["object","null"],"description":"Present when the agent has asked a question and the run is AWAITING_HUMAN. Answering is a PORTAL action — the API is read-only on runs.","properties":{"question":{"type":["string","null"]},"header":{"type":["string","null"]},"options":{"type":"array","items":{"type":"object"}}}}}}]}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/runs/{runId}/result":{"get":{"operationId":"getRunResult","summary":"Run RESULT — what the engine decided","description":"The decision-packet summary (the 12-element acceptance packet, projected), the grounded document’s classification + axes, the regulated floors (the force-stop and the signature floor: held vs released), the per-figure numeric receipts, and the transition trace. Scope `runs:read`.","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The run result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunResult"}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/matters":{"get":{"operationId":"listMatters","summary":"List targetable matters for a vertical","description":"Explicit matter selection support for D3: org-scoped, non-CLOSED, newest-first. Choosing a COMPLETED matter re-opens it on next intake.","parameters":[{"name":"verticalId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Targetable matters.","content":{"application/json":{"schema":{"type":"object","required":["matters"],"properties":{"matters":{"type":"array","items":{"type":"object","required":["id","reference","completed"],"properties":{"id":{"type":"string"},"reference":{"type":"string"},"title":{"type":"string"},"completed":{"type":"boolean"},"wouldAdopt":{"type":"boolean","description":"The row the PORTAL's default path would adopt — informational only; the API never adopts implicitly (D3)."}}}}}}}}},"400":{"description":"invalid_request | matter_targeting_required (matterId XOR newMatter is required — the Intake API never adopts a matter implicitly)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"unauthorized — missing, malformed, unknown, revoked, or expired API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"forbidden_scope — the key does not carry the scope this endpoint requires","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"rate_limited — fixed window per key, 600 requests/minute (provisional; the durable gateway limiter lands with platform-spine S3). Honors Retry-After.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}}}