HTTP API reference¶
The InCheck public surface is a thin REST API behind a single gateway. Two resource families:
/documents/*— manage Pods, your per-tenant document collections./chat— two-mode chat. EMS mode (omitorg_id) answers from general EMS knowledge; unified mode (setorg_idto a Pod) grounds the answer in your documents.
Every request needs Authorization: Bearer <your-api-key>.
| Environment | Base URL |
|---|---|
production |
https://api.incheck.ai |
staging |
https://api-acceptance.incheck.ai |
Namespace scoping
Your API key is bound to one InCheck organization. The gateway
derives a slug-form namespace (lowercase alphanumeric) from
your subdomain, and every org_id you push to or chat against
must start with <namespace>_. Cross-namespace requests are
rejected with 403.
Documents¶
POST /documents/initiate-upload¶
Request a presigned S3 POST for one or more files.
Body
| Field | Type | Notes |
|---|---|---|
org_id |
string | Hierarchical ^[a-z0-9]+(_[a-z0-9]+){1,9}$; first segment must be your namespace. |
filenames |
string[] | 1–20 entries. Allowed extensions: .pdf .docx .pptx .xlsx. |
batch_size |
integer | Chunk-batch size, 1–20. Default 6. |
Response 201
{
"job_id": "0c9de398-…",
"org_name": "acme",
"org_id": "acme_dispatch",
"version": "20260511_204108",
"s3_folder": "acme/acme_dispatch/2026/05/11/20260511_204108",
"upload_urls": [
{
"filename": "sop.pdf",
"upload_url": "https://…s3.amazonaws.com/",
"upload_fields": { "key": "…", "x-amz-algorithm": "…", "…": "…" },
"s3_key": "acme/acme_dispatch/.../originals/sop.pdf"
}
],
"expires_in": 3600,
"created_at": "2026-05-11T20:41:08Z"
}
To upload, POST the file to upload_url with every field from
upload_fields as multipart form data, plus file as the binary
payload. S3 returns 204 No Content on success.
POST /documents/complete-upload¶
Confirm the files landed and trigger processing.
Body
Response 200
{
"job_id": "0c9de398-…",
"status": "pending",
"org_name": "acme",
"org_id": "acme_dispatch",
"version": "20260511_204108",
"s3_folder": "acme/acme_dispatch/...",
"files_confirmed": ["sop.pdf"],
"created_at": "2026-05-11T20:41:10Z"
}
PUT /documents/orgs/{org_id}/documents/initiate¶
Add or replace files in an existing org_id. Files already in the
current version that you don't list are kept; files you list are
overwritten.
Body
Response 201
Returns the same shape as initiate-upload, plus
existing_documents_to_keep.
PUT /documents/orgs/{org_id}/documents/complete¶
Confirm an update and trigger processing of the merged version.
Body
GET /documents/orgs¶
List the org_ids you own.
Response 200
{
"org_ids": [
{
"org_id": "acme_dispatch",
"org_name": "acme",
"current_version": "20260511_204108",
"document_count": 1,
"last_updated": "2026-05-11T20:54:20Z"
}
],
"total_count": 1,
"filtered_by": "acme"
}
filtered_by is your derived namespace — use it to validate config
before issuing other calls.
GET /documents/orgs/{org_id}/documents¶
List documents in the current version, with short-lived presigned GET URLs.
Response 200
{
"org_id": "acme_dispatch",
"version": "20260511_204108",
"document_count": 1,
"documents": [
{
"filename": "sop.pdf",
"size_bytes": 4364,
"last_modified": "2026-05-11T20:41:10Z",
"presigned_url": "https://…",
"url_expires_in": 3600
}
],
"job_id": "…",
"s3_folder": "acme/acme_dispatch/..."
}
GET /documents/orgs/{org_id}/version¶
Returns the current version pointer for an org_id.
DELETE /documents/orgs/{org_id}/versions/{version}¶
Delete a specific version. Irreversible.
DELETE /documents/orgs/{org_id}¶
Delete the entire org_id and every version under it. Irreversible.
GET /documents/job/{job_id}¶
Status snapshot for a processing job.
Response 200
{
"job_id": "0c9de398-…",
"status": "completed",
"org_name": "acme",
"org_id": "acme_dispatch",
"version": "20260511_204108",
"progress": {
"total_documents": 1,
"total_pages": 3,
"processed_pages": 3
},
"error": null,
"created_at": "2026-05-11T20:41:10Z",
"completed_at": "2026-05-11T20:43:00Z"
}
status values: initiated, pending, processing, completed,
failed. Poll until terminal; typical small-PDF runs take ~90–120
seconds.
Chat¶
POST /chat¶
Send a chat message. Two modes:
- EMS mode (no
org_idin the body): the model answers from general EMS knowledge underscope/state. No retrieval. - Unified mode (
org_idset to a Pod you've onboarded): the model answers grounded in the documents in that Pod.
Body — EMS mode
{
"conversation_id": "ems-1",
"user_id": "alice@hospital.org",
"streaming": false,
"content": "Adult dose of atropine for symptomatic bradycardia?",
"scope": "ALS",
"state": "Massachusetts"
}
Body — Unified mode
{
"conversation_id": "uni-1",
"user_id": "alice@hospital.org",
"org_id": "acme_dispatch",
"streaming": false,
"content": "Summarize our dispatch escalation SOP.",
"scope": "ALS",
"state": "Massachusetts",
"conversation_hx": null
}
| Field | Type | Required | Notes |
|---|---|---|---|
conversation_id |
string | yes | Your conversation identifier (≥3 chars). |
user_id |
string | yes | Free-form end-user id (audit trail only). |
content |
string | yes | The user message (1–10000 chars). |
scope |
string | yes | EMS scope — "ALS", "BLS", etc. |
state |
string | yes | US state for state-specific protocols. |
org_id |
string | no | If present → unified mode. Hierarchical; first segment must be your namespace. If absent or empty string → EMS mode. |
streaming |
bool | no (default true) |
When false, response is one final SSE event; when true, streamed. |
conversation_hx |
string | null | no | Optional prior context. |
Response 200 (text/event-stream)
When streaming=true you receive many data: chunks before the final
{"type": "complete"} marker. When streaming=false you still receive
SSE — but the content is delivered in one event.
Mode hint on errors¶
Missing or invalid required fields come back as 400 with a hint that
spells out the contract — you don't have to consult docs to fix the
call:
{"detail":"scope: Field required; state: Field required.
Required fields: content, conversation_id, user_id, scope, state.
org_id is optional — include it (hierarchical, starting with your
namespace) to run in unified mode with retrieval against your
ingested documents; omit it for EMS mode (general protocol answers)."}
Errors¶
Errors come back as JSON: {"detail": "<message>"}.
| Status | When |
|---|---|
400 |
The body failed schema validation (malformed org_id, etc.) |
401 |
Missing / invalid / revoked API key |
403 |
Namespace mismatch on org_id, or your org isn't configured for the customer API |
404 |
Job, version, or org_id not found |
429 |
Per-key rate limit — respect Retry-After |
5xx |
Upstream failure — safe to retry with backoff |
Rate limits¶
Per-key rate limits are enforced on every endpoint, keyed by your API
key's underlying tenant. 429 responses include a Retry-After header.