Skip to content

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 (omit org_id) answers from general EMS knowledge; unified mode (set org_id to 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

{
  "org_id": "acme_dispatch",
  "filenames": ["sop.pdf", "policies.docx"],
  "batch_size": 6
}
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

{
  "job_id": "0c9de398-…",
  "uploaded_files": ["sop.pdf"]
}

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

{ "filenames": ["new_protocol.pdf"], "batch_size": 6 }

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

{ "job_id": "…", "uploaded_files": ["new_protocol.pdf"] }

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_id in the body): the model answers from general EMS knowledge under scope / state. No retrieval.
  • Unified mode (org_id set 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)

data: {"content": "Per our SOP, ..."}

data: {"type": "complete"}

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.


Back to home Python SDK →