# Klarefi Full Docs This file combines the public Klarefi documentation for LLM and agent use. --- URL: https://www.klarefi.com/docs Markdown: https://www.klarefi.com/docs-source # Klarefi Documentation Turn applicant documents and answers into structured, evidence-backed facts you can act on. Source: https://www.klarefi.com/docs ## Klarefi Klarefi helps regulated teams complete cases before they reach human review. You send applicant answers and documents, Klarefi resolves the facts a workflow needs, and every fact is linked back to evidence. The case is the record you read for current state. Hosted Intake, operator review, webhooks, and handoff packages all use the same case. Case Case --> Facts Facts --> Package Case -. state changed .-> Event Event -. notify .-> System `} /> ## Core Objects - **Case** - one intake packet moving from submission to review. Read it from the API any time for current state. - **Session** - a signed applicant-facing intake flow for a case. - **Fact** - a structured value the workflow needs, such as `incident_date`. - **Evidence** - the source quote or document span that supports a fact. - **Event** - a webhook signal that case state changed. - **Package** - a review-ready handoff view for a case. ## Primary Integration Start with Hosted Intake. Your backend creates a session, redirects the applicant to Klarefi, and reads the completed case when the intake is ready. Hosted Intake covers the applicant-facing flow: form steps, uploads, follow-up questions, submission, webhooks, and the cited case package operators review. ## Use These Docs With AI - [LLM index](/llms.txt) - short map of the docs corpus - [Full LLM text](/llms-full.txt) - complete docs content for agents - [API quickstart Markdown](/docs-source/api/quickstart) - one page source - [Developer setup](/docs/api/sdks#install) - install the SDK, CLI, and agent-readable docs ## Start Building - [Hosted Intake](/docs/guides/hosted-intake) - Build the applicant-facing intake session - [Create an Intake Session](/docs/guides/create-intake-session) - Generate a signed hosted intake URL - [Upload Documents](/docs/guides/upload-documents) - Attach documents to a case context - [Hosted Intake Quickstart](/docs/guides/hosted-intake-quickstart) - Create a session and redirect an applicant - [SDKs and CLI](/docs/api/sdks) - Install `@klarefi/node` and the `klarefi` CLI - [Webhook Setup](/docs/guides/webhooks) - Receive signed event deliveries - [Cases Reference](/docs/reference/cases) - Case statuses and review states - [API Quickstart](/docs/api/quickstart) - Create a hosted intake session and read the case - [API Reference](/docs/api/reference) - Compact endpoint reference ## What To Rely On - Read the case when you need current state. Treat webhooks as notifications. - Every document-derived fact should carry evidence. - Facts without enough support route to follow-up or human review. - Use the case package as the operator handoff. --- URL: https://www.klarefi.com/docs/api/authentication Markdown: https://www.klarefi.com/docs-source/api/authentication # Authentication API keys, environments, scopes, and identity checks Source: https://www.klarefi.com/docs/api/authentication > **API base URL** > > Use `https://www.klarefi.com`, or the direct regional Convex HTTP URL shown > in **Settings → Developer**. Send API keys as Bearer tokens: ```http Authorization: Bearer sk_test_your_api_key ``` API keys are generated in the dashboard Developer settings. The plaintext key is shown once and stored by Klarefi only as a hash. ## Environments Key prefixes select the environment: | Prefix | Environment | Use for | | ---------- | ----------- | ------------------------------- | | `sk_test_` | `test` | Development and integration UAT | | `sk_live_` | `live` | Production traffic | Live keys can be gated by billing, legal acceptance, and live usage limits. Those gates return `402 billing_error` or `428 precondition_error`. ## Scopes Keys carry scopes. The public v1 scopes are: | Scope | Allows | | ------------------------ | --------------------------------------------------------- | | `cases:process` | `POST /api/v1/process` | | `cases:read` | Case, workspace, command, package, event, and `/me` reads | | `cases:review` | Operator queue and intake-review command submission | | `connectors:read` | Connector list and detail reads | | `connectors:write` | Connector create, import, and delete | | `intake:sessions:create` | `POST /api/v1/sessions` | | `privacy:erase` | `DELETE /api/v1/documents/\{docId\}` | | `workflows:read` | `GET /api/v1/workflows` | | `workflows:write` | Save workflow drafts with `POST /api/v1/workflows` | | `webhooks:test` | `POST /api/v1/webhooks/test` | | `webhooks:acknowledge` | Webhook delivery acknowledgements | Missing or invalid keys return `401 authentication_error`. Valid keys without the required scope return `403 authorization_error`. Test keys silently drop `privacy:erase` before authorization, even if the stored key includes it. The default test scope set includes `cases:process`, `cases:read`, `connectors:read`, `connectors:write`, `intake:sessions:create`, and `webhooks:test`; it excludes `webhooks:acknowledge`. ## Rotating keys Rotate a key from **Settings → Developer**. Klarefi creates a replacement with the same environment and scopes, shows the new secret once, and keeps the old key valid for a 24-hour grace period. Update every client during that window. After the grace period the old key expires and returns `401 invalid_api_key`. ## Identify the key ```bash curl "$KLAREFI_API_BASE_URL/api/v1/me" \ -H "Authorization: Bearer $KLAREFI_API_KEY" ``` ```json { "org_id": "org_abc123", "environment": "test" } ``` ## Health check `GET /api/v1/health` does not require authentication. ```bash curl "$KLAREFI_API_BASE_URL/api/v1/health" ``` ```json { "status": "ok" } ``` --- URL: https://www.klarefi.com/docs/api/cases Markdown: https://www.klarefi.com/docs-source/api/cases # Cases API Read case status, intake events, and decision-ready handoff packages Source: https://www.klarefi.com/docs/api/cases > **Use your workspace API base URL** > > Set `KLAREFI_API_BASE_URL` to the value in **Settings → Developer** before > using the examples. Current workspaces may use a > `https://.convex.site` URL. Case reads expose Klarefi's current case record. Use them when your system needs current data; use webhooks as notifications that a read may be useful. `runtime_status` uses the runtime vocabulary: `processing`, `intake_in_progress`, `gate_ready`, `gate_evaluating`, `ready_for_review`, or `complete`. ## GET /api/v1/cases/\{caseId\} Required scope: `cases:read` ```bash curl "$KLAREFI_API_BASE_URL/api/v1/cases/case_abc123" \ -H "Authorization: Bearer $KLAREFI_API_KEY" ``` The response is the current operator workspace projection. Unknown fields should be ignored. ```json { "case_id": "case_abc123", "case_type_id": "motor_claim", "workflow_id": "motor_claim_intake", "workflow_version": "3", "operating_mode": "system_of_action", "runtime_status": "ready_for_review", "workspace": { "current_node_id": "review", "current_node_title": "Operator review", "current_gate_id": null, "current_gate_title": null, "next_action": null, "operational_summary": { "reason": { "why_here": "Ready for operator review.", "why_here_code": "ready_for_review", "queue_category": "ready_for_review", "queue_status": "ready_for_review", "current_gate_id": null, "current_node_id": "review" }, "blocking_items": [], "primary_action": null, "ownership": null, "blocking_fact_ids": [], "has_needs_input_facts": false, "has_failed_facts": false }, "facts": [ { "fact_id": "incident_date", "fact_label": "Incident date", "status": "resolved", "value": "2023-02-03", "candidate_id": "cand_001", "anchor_match_quality": 1, "source": "document", "reason_codes": [], "provenance": [ { "kind": "document_span", "document_id": "doc_def456", "page_index": 0, "quote": "3 februari 2023", "char_start": 142, "char_end": 158 } ], "verification_metadata": [], "evidence": [ { "evidence_id": "ev_001", "quote": "3 februari 2023", "location": { "char_start": 142, "char_end": 158 }, "doc_id": "doc_def456", "page_index": 0 } ] } ], "documents": [ { "doc_id": "doc_def456", "status": "processed", "mime_type": "application/pdf" } ], "structured_inputs": {}, "summary": { "resolved_count": 1, "needs_input_count": 0, "failed_count": 0, "document_count": 1 }, "jobs": [], "intake_session": null }, "queue_row": { "case_id": "case_abc123", "operating_mode": "system_of_action", "runtime_status": "ready_for_review", "queue_category": "ready_for_review", "queue_status": "ready_for_review", "why_here": "Ready for operator review.", "why_here_code": "ready_for_review", "blocking_items": [], "blocking_fact_ids": [], "sort_timestamp": "2026-01-15T10:30:00.000Z", "updated_at": "2026-01-15T10:30:00.000Z" }, "updated_at": "2026-01-15T10:30:00.000Z" } ``` ## GET /api/v1/cases/\{caseId\}/events Required scope: `cases:read` Query parameters: `after_cursor`, default `0`; `limit`, default `100`, maximum `500`. ```bash curl "$KLAREFI_API_BASE_URL/api/v1/cases/case_abc123/events?after_cursor=0" \ -H "Authorization: Bearer $KLAREFI_API_KEY" ``` ```json { "case_id": "case_abc123", "events": [ { "event_id": "evt_001", "event_type": "document.uploaded", "cursor": 1, "created_at": "2026-01-15T10:30:00.000Z" } ], "next_cursor": 1, "has_more": false } ``` Store `next_cursor` and pass it as `after_cursor` on the next poll. When `has_more` is `true`, poll again immediately with `after_cursor` set to `next_cursor` to drain the next page. ## GET /api/v1/cases/\{caseId\}/package Required scope: `cases:read` ```bash curl "$KLAREFI_API_BASE_URL/api/v1/cases/case_abc123/package" \ -H "Authorization: Bearer $KLAREFI_API_KEY" ``` ```json { "case_id": "case_abc123", "case_file_url": "https://app.klarefi.com/case-file/case_abc123?token=...", "facts": {}, "documents": [] } ``` `case_file_url` may be `null` if a signed case-file link cannot be created for the deployment. --- URL: https://www.klarefi.com/docs/api/documents Markdown: https://www.klarefi.com/docs-source/api/documents # Documents API Erase uploaded documents and derived data for privacy workflows Source: https://www.klarefi.com/docs/api/documents > **Use your workspace API base URL** > > Set `KLAREFI_API_BASE_URL` to the value in **Settings → Developer** before > using the example. Current workspaces may use a > `https://.convex.site` URL. ## DELETE /api/v1/documents/\{docId\} Required scope: `privacy:erase` ```bash curl -X DELETE "$KLAREFI_API_BASE_URL/api/v1/documents/doc_def456" \ -H "Authorization: Bearer $KLAREFI_API_KEY" ``` The erasure job deletes the document and derived evidence, candidates, case document links, artifacts, uploads, and storage objects where present. `200` means the erasure completed in the current batch. `202` means Klarefi deleted a batch and more work remains. ```json { "erasure_receipt": { "doc_id": "doc_def456", "audit_id": "audit_001", "completed": true, "batch_count": 8, "batch_limit": 100, "remaining_budget": 92, "deleted_evidence": 5, "deleted_candidates": 3, "deleted_case_documents": 1, "deleted_artifacts": 2, "deleted_uploads": 1, "deleted_storage_objects": 1, "deleted_document": true, "timestamp": "2026-01-15T10:30:00.000Z" } } ``` If the document does not exist for the organization, Klarefi returns `404 not_found`. --- URL: https://www.klarefi.com/docs/api/errors Markdown: https://www.klarefi.com/docs-source/api/errors # Errors Error response format, status codes, rate limits, and retry guidance Source: https://www.klarefi.com/docs/api/errors Every API error response uses this envelope: ```json { "error": { "type": "validation_error", "code": "missing_document_url", "message": "document_url is required and must be a string", "request_id": "req_abc123def456" } } ``` ## Error types | Type | Status | Meaning | | ---------------------- | ------ | ------------------------------------------------------- | | `authentication_error` | 401 | Missing, invalid, expired, or revoked key | | `authorization_error` | 403 | Key is valid but lacks the required scope | | `validation_error` | 400 | Invalid body, path, query, or idempotency use | | `not_found` | 404 | Resource does not exist for the organization | | `billing_error` | 402 | Live billing or live usage gate blocked | | `precondition_error` | 428 | Required legal or setup precondition missing | | `rate_limit_error` | 429 | Per-key rate limit or monthly test-session cap exceeded | | `internal_error` | 500 | Unexpected Klarefi server error | ## Error codes | Code | HTTP status | Meaning | What to do | | --------------------------------------- | ----------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------ | | `invalid_api_key` | 401 | The API key is missing, invalid, revoked, expired, or for the wrong environment | Replace an expired/rotated key or send a valid key | | `forbidden` | 403 | The key does not have the required scope | Add the scope or use a key with the right access | | `billing_required` | 402 | Live API access requires active billing | Enable billing or use a test key | | `live_usage_cap_exceeded` | 402 | The workspace reached its live usage cap | Wait for the cap to reset or increase the cap | | `precondition_required` | 428 | A required account precondition is missing | Complete the setup step described in the message | | `legal_acceptance_required` | 428 | Current self-serve legal terms must be accepted | Accept the current terms before live API access | | `self_serve_webhooks_disabled` | 428 | Self-serve webhook delivery is disabled for this deployment | Enable self-serve webhooks before creating intake work | | `rate_limit_exceeded` | 429 | The key exceeded its per-minute rate limit | Retry after `Retry-After` or `X-RateLimit-Reset` | | `test_usage_cap_exceeded` | 429 | The workspace created 50 test API sessions in the current UTC month | Wait for the next month before creating another test session | | `invalid_request` | 400 | The request is malformed | Fix the body, path, query, or headers | | `invalid_json` | 400 | The JSON body could not be parsed | Send valid JSON with `Content-Type: application/json` | | `invalid_connector_payload` | 400 | Connector manifest validation failed | Fix the connector fields in the response message | | `missing_connector_key` | 400 | A connector key was required but missing | Provide `connector_key` or the path connector key | | `openapi_fetch_failed` | 400 | The OpenAPI URL could not be fetched | Confirm the URL is public HTTPS and returns JSON | | `missing_openapi_spec` | 400 | No OpenAPI source was provided | Send `openapi_url`, `openapi_json`, or `openapi` | | `openapi_yaml_not_supported` | 400 | YAML OpenAPI specs are not accepted | Convert the spec to JSON before import | | `invalid_openapi_spec` | 400 | The OpenAPI document could not be parsed | Send a valid OpenAPI JSON document | | `invalid_openapi_connector` | 400 | A connector could not be generated from the spec | Fix unsupported or incomplete connector metadata | | `missing_document_url` | 400 | `document_url` is missing from `POST /process` | Send a public HTTPS document URL | | `invalid_document_url` | 400 | `document_url` is not an allowed public HTTPS URL | Use a public HTTPS URL, not private or local network | | `per_request_webhook_url_not_supported` | 400 | Per-request webhook URLs are not supported | Register webhook endpoints in the dashboard | | `missing_case_type_id` | 400 | `case_type_id` is required | Send a configured case type ID | | `invalid_integration_identity` | 400 | Integration identity fields are invalid | Send non-empty string identity fields | | `missing_idempotency_key` | 400 | An idempotency key is required | Send `idempotency_key` or `Idempotency-Key` | | `idempotency_key_conflict` | 400 | The idempotency key was reused with different parameters | Generate a new key for different work | | `external_case_id_conflict` | 400 | The external case ID already exists for the organization | Use the existing case or a different external ID | | `missing_case_id` | 400 | A case ID path segment is missing | Include the case ID in the URL | | `invalid_after_cursor` | 400 | `after_cursor` is not an integer | Send an integer cursor, usually the previous `next_cursor` | | `invalid_limit` | 400 | `limit` is outside the allowed event page size range | Send an integer from 1 to 500 | | `missing_doc_id` | 400 | A document ID path segment is missing | Include the document ID in the URL | | `invalid_acknowledgement_payload` | 400 | Webhook acknowledgement payload validation failed | Send a valid `acknowledgement_id` or omit it | | `webhook_delivery_not_delivered` | 400 | The delivery cannot be acknowledged before a successful delivery | Wait until the delivery status is delivered | | `missing_endpoint_url` | 400 | `endpoint_url` is missing from webhook test | Send a public HTTPS receiver URL | | `missing_signing_secret` | 400 | `signing_secret` is missing from webhook test | Send the endpoint signing secret | | `invalid_endpoint_url` | 400 | `endpoint_url` is not an allowed public HTTPS URL | Use a public HTTPS URL, not private or local network | | `not_found` | 404 | The requested resource was not found for the organization | Check the ID and environment | | `internal_error` | 500 | Klarefi hit an unexpected server error | Retry if safe and contact support with `request_id` | ## Rate limits Authenticated success responses include: | Header | Description | | ----------------------- | ------------------------------------ | | `X-RateLimit-Limit` | Maximum requests per minute | | `X-RateLimit-Remaining` | Remaining requests in the window | | `X-RateLimit-Reset` | Epoch seconds when the window resets | When the limit is exceeded, Klarefi returns `429 rate_limit_error` and may include `Retry-After`. The 429 response also includes the same `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. ## Retry guidance Retry `429` responses after the rate-limit reset. Retry network failures with the same idempotency key. Do not blindly retry `400`, `401`, `403`, `404`, `402`, or `428` responses; those require a request or account-state change. The test-session monthly cap resets at the start of each UTC calendar month. Idempotent replays do not consume another session from the cap. --- URL: https://www.klarefi.com/docs/api/idempotency Markdown: https://www.klarefi.com/docs-source/api/idempotency # Idempotency Safely retry session creation, document processing, and webhook acknowledgement requests Source: https://www.klarefi.com/docs/api/idempotency Use idempotency when retrying API calls after timeouts or network errors. `POST /api/v1/sessions` and `POST /api/v1/process` require an idempotency key. Send it in either place: ```http Idempotency-Key: session_claim_12345 ``` ```json { "idempotency_key": "session_claim_12345" } ``` If both are present, the JSON body value wins. ## Replays Repeating the same request with the same key returns the original resource with HTTP `200` and `idempotent_replay: true`. ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400...?token=...", "expires_at": "2026-01-22T10:30:00.000Z", "idempotent_replay": true } ``` ## Conflicts Reusing an idempotency key with different request parameters returns a validation error: ```json { "error": { "type": "validation_error", "code": "idempotency_key_conflict", "message": "idempotency_key was already used with different request parameters", "request_id": "req_abc123def456" } } ``` ## Webhook acknowledgements `POST /api/v1/webhooks/deliveries/\{deliveryId\}/ack` accepts an optional `acknowledgement_id` body field. If it is omitted, Klarefi uses the `Idempotency-Key` header when present. --- URL: https://www.klarefi.com/docs/api/overview Markdown: https://www.klarefi.com/docs-source/api/overview # API Overview How Klarefi's public API is organized for hosted intake and review-ready case files Source: https://www.klarefi.com/docs/api/overview The Klarefi API lets customer systems create hosted intake sessions, read current case state, receive signed webhooks, and erase document-derived data. > **Canonical API base URL** > > Production integrations use the canonical host below. A workspace-specific > `https://.convex.site` URL from **Settings → Developer** also > remains valid for direct regional access. Examples use this illustrative value: ```text https://www.klarefi.com ``` Download the [OpenAPI 3.1 specification](/docs/api/openapi.yaml) for the full machine-readable contract. For TypeScript integrations, install the SDK and verify your credentials: ```bash npm install @klarefi/node export KLAREFI_API_KEY="sk_test_..." npx klarefi doctor ``` ## Versioning Public endpoints live under `/api/v1`. Additive response fields may appear over time. Integrations should ignore unknown fields and use documented IDs, statuses, and event names for control flow. ## Integration Model Hosted Intake is the integration path. Klarefi owns the applicant-facing flow: form steps, uploads, follow-up questions, and submission. Create a session, redirect the applicant to `signed_url`, then consume the case through webhooks or case reads. Redirect Redirect --> Intake Intake --> Case Case --> Facts[Facts with evidence] Case --> Events[Signed webhooks] Case --> Package `} /> ## Common Conventions - Requests and responses are JSON unless an endpoint says otherwise. - API keys are sent with `Authorization: Bearer sk_live_...` or `Authorization: Bearer sk_test_...`. - `POST /api/v1/sessions` requires an `idempotency_key` in the JSON body or an `Idempotency-Key` header. - Authenticated success responses include `X-RateLimit-*` headers. - Error responses use the shared `{ "error": { ... } }` envelope. --- URL: https://www.klarefi.com/docs/api/quickstart Markdown: https://www.klarefi.com/docs-source/api/quickstart # API Quickstart Create a hosted intake session, redirect an applicant, and read the resulting case Source: https://www.klarefi.com/docs/api/quickstart > **Canonical API base URL** > > Production integrations use `https://www.klarefi.com`. A workspace-specific > Convex HTTP URL shown in **Settings → Developer** remains valid for direct > regional access. Set your API key: ```bash export KLAREFI_API_KEY="sk_test_..." export KLAREFI_BASE_URL="https://www.klarefi.com" export KLAREFI_API_BASE_URL="https://www.klarefi.com" ``` `@klarefi/node` reads `KLAREFI_BASE_URL`. The CLI and curl examples use `KLAREFI_API_BASE_URL`; set both to the dashboard value. ## Install ```bash npm install @klarefi/node npx klarefi doctor ``` ## Hosted intake Create a session when Klarefi should own the applicant-facing intake. Run this from trusted server-side code, then redirect the applicant to `signed_url`. ```ts import { Klarefi } from "@klarefi/node"; const klarefi = new Klarefi({ apiKey: process.env.KLAREFI_API_KEY!, }); const session = await klarefi.sessions.create({ case_type_id: "motor_claim", external_case_id: "claim_12345", external_applicant_id: "applicant_789", idempotency_key: "session_claim_12345", ttl_hours: 168, locale: "nl", }); return Response.redirect(session.signed_url, 303); ``` For a CLI smoke test: ```bash npx klarefi sessions create \ --case-type motor_claim \ --external-case-id claim_12345 \ --external-applicant-id applicant_789 \ --url-only ``` The same request over HTTP: ```bash curl -X POST "$KLAREFI_API_BASE_URL/api/v1/sessions" \ -H "Authorization: Bearer $KLAREFI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "case_type_id": "motor_claim", "idempotency_key": "session_claim_12345", "external_case_id": "claim_12345", "external_applicant_id": "applicant_789", "ttl_hours": 168, "locale": "nl" }' ``` Example response: ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400-e29b-41d4-a716-446655440000?token=...", "access_token": "1751879000.9f2c4e...", "access_token_expires_at": "2026-01-22T10:30:00.000Z", "expires_at": "2026-01-22T10:30:00.000Z", "agent": { "api_base": "https://your-deployment.convex.site/api/v1", "manifest_url": "https://your-deployment.convex.site/api/v1/sessions/550e8400-e29b-41d4-a716-446655440000/manifest", "access_token": "1751879000.9f2c4e..." }, "snippet": "", "idempotent_replay": false } ``` For v1 hosted sessions, the session ID is also the case ID used by case reads. ```ts const caseFile = await klarefi.cases.retrieve(session.session_id); const events = await klarefi.cases.events(session.session_id, { afterCursor: 0, limit: 100, }); const pkg = await klarefi.cases.getPackage(session.session_id); ``` CLI equivalents: ```bash npx klarefi cases get 550e8400-e29b-41d4-a716-446655440000 npx klarefi cases events 550e8400-e29b-41d4-a716-446655440000 --after-cursor 0 npx klarefi cases package 550e8400-e29b-41d4-a716-446655440000 ``` --- URL: https://www.klarefi.com/docs/api/reference Markdown: https://www.klarefi.com/docs-source/api/reference # API Reference Compact endpoint reference for Klarefi API v1 Source: https://www.klarefi.com/docs/api/reference > **API base URL** > > Examples use `https://www.klarefi.com`. The direct regional Convex HTTP URL > shown in **Settings → Developer** is also supported. ## Endpoints | Method | Path | Scope | Purpose | | ------ | ------------------------------------------------- | ------------------------ | --------------------------------- | | GET | `/api/v1/health` | None | Service health check | | GET | `/api/v1/me` | `cases:read` | Identify the authenticated org | | POST | `/api/v1/sessions` | `intake:sessions:create` | Create a hosted intake session | | POST | `/api/v1/sessions/\{sessionId\}/handoff` | `intake:sessions:create` | Complete customer-side handoff | | POST | `/api/v1/process` | `cases:process` | Queue a document for processing | | GET | `/api/v1/connectors` | `connectors:read/write` | List configured connectors | | POST | `/api/v1/connectors` | `connectors:write` | Create or update a connector | | POST | `/api/v1/connectors/import` | `connectors:write` | Import an OpenAPI connector | | GET | `/api/v1/connectors/\{connectorKey\}` | `connectors:read/write` | Read a configured connector | | DELETE | `/api/v1/connectors/\{connectorKey\}` | `connectors:write` | Delete a configured connector | | GET | `/api/v1/cases/\{caseId\}` | `cases:read` | Read current case state | | GET | `/api/v1/cases/\{caseId\}/events` | `cases:read` | Poll intake events | | GET | `/api/v1/cases/\{caseId\}/package` | `cases:read` | Read the decision-ready package | | GET | `/api/v1/cases/\{caseId\}/workspace` | `cases:read` | Read the cited operator workspace | | POST | `/api/v1/cases/\{caseId\}/commands` | `cases:review` | Submit an intake-review command | | GET | `/api/v1/cases/\{caseId\}/commands/\{commandId\}` | `cases:read` | Poll an intake-review command | | GET | `/api/v1/operator/queue` | `cases:review` | List the bounded operator queue | | GET | `/api/v1/workflows` | `workflows:read` | List workflow declarations | | POST | `/api/v1/workflows` | `workflows:write` | Save a workflow draft | | DELETE | `/api/v1/documents/\{docId\}` | `privacy:erase` | Erase a document and derived data | | POST | `/api/v1/webhooks/test` | `webhooks:test` | Send a signed test delivery | | POST | `/api/v1/webhooks/deliveries/\{deliveryId\}/ack` | `webhooks:acknowledge` | Acknowledge webhook handoff | ## GET /api/v1/health ```json { "status": "ok" } ``` ## GET /api/v1/me ```json { "org_id": "org_abc123", "environment": "test" } ``` ## POST /api/v1/sessions ```json { "case_type_id": "motor_claim", "idempotency_key": "session_claim_12345", "external_case_id": "claim_12345", "ttl_hours": 168, "locale": "nl" } ``` ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400...?token=...", "access_token": "1751879000.9f2c4e...", "access_token_expires_at": "2026-01-22T10:30:00.000Z", "expires_at": "2026-01-22T10:30:00.000Z", "agent": { "api_base": "https://your-deployment.convex.site/api/v1", "manifest_url": "https://your-deployment.convex.site/api/v1/sessions/550e8400-e29b-41d4-a716-446655440000/manifest", "access_token": "1751879000.9f2c4e..." }, "snippet": "", "idempotent_replay": false } ``` `snippet` is a redirect helper that sends the browser to `signed_url`. For on-site embedding, use the [embed script](/docs/guides/embed). Errors: `400 invalid_json`, `missing_case_type_id`, idempotency, external ID, or prefill validation errors; `401 invalid_api_key`; `403 forbidden`; `402` billing errors; `428` account preconditions; `429 rate_limit_exceeded`; `500 internal_error`. ## POST /api/v1/sessions/\{sessionId\}/handoff Required scope: `intake:sessions:create`. The body is optional. To ask Klarefi to send the applicant a continue link: ```json { "notify": { "email": "applicant@example.com" } } ``` ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400...?token=...", "access_token": "1751879000.9f2c4e...", "access_token_expires_at": "2026-01-22T10:30:00.000Z", "expires_at": "2026-01-22T10:30:00.000Z", "customer_completed_at": "2026-01-15T10:30:00.000Z", "already_marked": false, "notification": { "requested": true, "sent": true } } ``` `notification` is omitted when `notify` is absent. A failed email does not fail the handoff; it returns `sent: false` with `email_not_configured` or `send_failed`. Errors: `400 validation_error`; `401 invalid_api_key`; `403 forbidden`; `404 session_not_found`; `409 session_terminal`; `429 rate_limit_exceeded`; `500 internal_error`. ## POST /api/v1/process Required scope: `cases:process`. ```json { "document_url": "https://files.example.com/claim.pdf", "case_type_id": "motor_claim", "idempotency_key": "process_claim_12345", "external_case_id": "claim_12345", "external_applicant_id": "applicant_789", "external_customer_id": "customer_456", "trace_id": "trace_claim_12345" } ``` ```json { "case_id": "case_abc123", "doc_id": "doc_def456", "job_id": "job_ghi789", "status": "queued", "idempotent_replay": false } ``` Returns `201` for new work and `200` for an idempotent replay. Errors: `400 invalid_json`, `missing_document_url`, `invalid_document_url`, `missing_case_type_id`, `per_request_webhook_url_not_supported`, integration identity, idempotency, or external ID errors; `401 invalid_api_key`; `403 forbidden`; `402` billing errors; `428` account or self-serve webhook preconditions; `429 rate_limit_exceeded`; `500 internal_error`. ## GET /api/v1/connectors Required scope: `connectors:read` or `connectors:write`. ```json { "connectors": [ { "connector_key": "claims_api", "label": "Claims API", "connector_type": "http_api", "transport": { "base_url": "https://claims.example.com", "timeout_ms": 10000, "default_headers": [], "auth": { "mode": "bearer_token", "has_secret": true, "has_username": false, "has_password": false } }, "functions": [] } ], "environment": "test" } ``` Stored transport secrets are never returned. Errors: `401 invalid_api_key`, `403 forbidden`, `429 rate_limit_exceeded`, `500 internal_error`. ## POST /api/v1/connectors Required scope: `connectors:write`. ```json { "connector_key": "claims_api", "label": "Claims API", "connector_type": "http_api", "transport": { "base_url": "https://claims.example.com", "timeout_ms": 10000, "auth": { "mode": "bearer_token", "secret": "secret-value" } }, "functions": [ { "function_key": "lookup_claim", "label": "Lookup claim", "usage": "verify_fact", "request": { "method": "GET", "path": "/claims/{{claim_id}}" }, "input_parameters": [ { "name": "claim_id", "type": "text", "required": true } ], "sample_input": { "claim_id": "CLM-123" }, "success_criteria": { "status_codes": [200] } } ] } ``` ```json { "connector_key": "claims_api", "created": true, "environment": "test" } ``` Returns `201` when created and `200` when updated. The connector is active immediately. Errors: `400 invalid_json` or `invalid_connector_payload`; `401 invalid_api_key`; `403 forbidden`; `429 rate_limit_exceeded`; `500 internal_error`. ## POST /api/v1/connectors/import Required scope: `connectors:write`. Provide `openapi_url`, `openapi_json`, or an inline `openapi` object. OpenAPI documents must be JSON; YAML imports are rejected. ```json { "connector_key": "claims_api", "label": "Claims API", "openapi_url": "https://claims.example.com/openapi.json", "auth": { "mode": "bearer_token", "secret": "secret-value" } } ``` ```json { "connector_key": "claims_api", "created": true, "environment": "test", "functions_imported": 4 } ``` Returns `201` when created and `200` when updated. Errors: `400 invalid_json`, `missing_connector_key`, `openapi_fetch_failed`, `missing_openapi_spec`, `openapi_yaml_not_supported`, `invalid_openapi_spec`, or `invalid_openapi_connector`; `401 invalid_api_key`; `403 forbidden`; `429 rate_limit_exceeded`; `500 internal_error`. ## GET /api/v1/connectors/\{connectorKey\} Required scope: `connectors:read` or `connectors:write`. ```json { "connector": { "connector_key": "claims_api", "label": "Claims API", "connector_type": "http_api", "transport": { "base_url": "https://claims.example.com", "timeout_ms": 10000, "default_headers": [], "auth": { "mode": "bearer_token", "has_secret": true } }, "functions": [] }, "environment": "test" } ``` Errors: `401 invalid_api_key`, `403 forbidden`, `404 not_found`, `429 rate_limit_exceeded`, `500 internal_error`. ## DELETE /api/v1/connectors/\{connectorKey\} Required scope: `connectors:write`. ```json { "connector_key": "claims_api", "deleted": true, "environment": "test" } ``` Errors: `401 invalid_api_key`, `403 forbidden`, `404 not_found`, `429 rate_limit_exceeded`, `500 internal_error`. ## GET /api/v1/cases/\{caseId\} The case response is the current operator workspace projection. Its `runtime_status` is one of `processing`, `intake_in_progress`, `gate_ready`, `gate_evaluating`, `ready_for_review`, or `complete`. Unknown fields are allowed. This abbreviated response shows the top-level nesting used by the projection. ```json { "case_id": "case_abc123", "case_type_id": "motor_claim", "workflow_id": "motor_claim_intake", "workflow_version": "3", "operating_mode": "system_of_action", "runtime_status": "ready_for_review", "workspace": { "facts": [ { "fact_id": "incident_date", "fact_label": "Incident date", "status": "resolved", "value": "2023-02-03", "reason_codes": [], "provenance": [ { "kind": "document_span", "document_id": "doc_def456", "page_index": 0, "quote": "3 februari 2023" } ], "verification_metadata": [], "evidence": [ { "evidence_id": "ev_001", "quote": "3 februari 2023", "location": { "char_start": 142, "char_end": 158 }, "doc_id": "doc_def456", "page_index": 0 } ] } ], "documents": [ { "doc_id": "doc_def456", "status": "processed", "mime_type": "application/pdf" } ] }, "updated_at": "2026-01-15T10:30:00.000Z" } ``` `POST /api/v1/cases/\{caseId\}/commands` requires an idempotency key in either the JSON `idempotency_key` field or the `Idempotency-Key` header. It accepts only intake-review commands and cannot complete or reopen a case. Workflow writes always save drafts; publishing remains human-controlled. ## GET /api/v1/cases/\{caseId\}/events Query parameters: `after_cursor`, default `0`; `limit`, default `100`, maximum `500`. ```json { "case_id": "case_abc123", "events": [ { "event_id": "evt_001", "event_type": "document.processed", "cursor": 1, "created_at": "2026-01-15T10:30:00Z" } ], "next_cursor": 1, "has_more": false } ``` ## GET /api/v1/cases/\{caseId\}/package The package response is workflow-shaped and includes `case_file_url`. ```json { "case_id": "case_abc123", "case_file_url": "https://app.klarefi.com/case-file/case_abc123?token=..." } ``` ## DELETE /api/v1/documents/\{docId\} Returns `200` when complete or `202` when another erasure batch remains. ```json { "erasure_receipt": { "doc_id": "doc_def456", "audit_id": "audit_001", "completed": true, "deleted_evidence": 5, "deleted_candidates": 3, "deleted_document": true, "timestamp": "2026-01-15T10:30:00.000Z" } } ``` ## POST /api/v1/webhooks/test ```json { "endpoint_url": "https://your-app.example.com/webhooks/klarefi", "signing_secret": "whsec_your_signing_secret" } ``` ```json { "success": true, "status_code": 200 } ``` ## POST /api/v1/webhooks/deliveries/\{deliveryId\}/ack ```json { "acknowledgement_id": "ack_claim_12345" } ``` ```json { "acknowledged": true, "already_acknowledged": false, "delivery_id": "whd_123" } ``` --- URL: https://www.klarefi.com/docs/api/sdks Markdown: https://www.klarefi.com/docs-source/api/sdks # SDKs and CLI Install the Klarefi TypeScript SDK and CLI Source: https://www.klarefi.com/docs/api/sdks Use the TypeScript SDK in application code. Use the CLI to check credentials, create test sessions, scaffold webhook handlers, and work with connector manifests. > **Canonical API base URL** > > Production integrations use `https://www.klarefi.com`. A workspace-specific > Convex HTTP URL shown in **Settings → Developer** remains valid for direct > regional access. ## Install ```bash npm install @klarefi/node npx klarefi --help ``` For repeated CLI use: ```bash npm install -g klarefi klarefi --help ``` The packages are: | Package | Use for | | --------------- | ---------------------------------------------------------------------------------------------- | | `@klarefi/node` | Server-side TypeScript calls, webhook signature helpers, and the applicant-plane intake client | | `klarefi` | Local setup, diagnostics, webhook tests, connector manifests | | `@klarefi/mcp` | Local fallback for the hosted stateless MCP endpoint at `https://www.klarefi.com/mcp` | To drive a hosted intake session with an agent, over REST, the SDK, or MCP, see [Agent intake](/docs/guides/agent-intake). ## Configure Set your API key in the environment: ```bash export KLAREFI_API_KEY="sk_test_..." export KLAREFI_BASE_URL="https://www.klarefi.com" export KLAREFI_API_BASE_URL="https://www.klarefi.com" ``` Then verify the key: ```bash npx klarefi whoami npx klarefi doctor ``` The TypeScript SDK reads `KLAREFI_BASE_URL`; the CLI reads `KLAREFI_API_BASE_URL`. Set both to the dashboard value. The SDK resolves its URL as constructor `baseUrl`, then `KLAREFI_BASE_URL`, then `https://www.klarefi.com` as the production fallback. You can also store the key for local CLI use: ```bash npx klarefi login --api-key sk_test_... ``` ## Create a hosted intake session Create the session from your backend and redirect the applicant to `signed_url`: ```ts import { Klarefi } from "@klarefi/node"; const klarefi = new Klarefi({ apiKey: process.env.KLAREFI_API_KEY!, }); const session = await klarefi.sessions.create({ case_type_id: "motor_claim", external_case_id: "claim_12345", external_applicant_id: "applicant_789", idempotency_key: "session_claim_12345", locale: "nl", }); console.log(session.signed_url); ``` For a quick local check: ```bash npx klarefi sessions create \ --case-type motor_claim \ --external-case-id claim_12345 \ --url-only ``` ## Read the case ```ts const caseFile = await klarefi.cases.retrieve(session.session_id); const events = await klarefi.cases.events(session.session_id, { afterCursor: 0, limit: 100, }); if (events.has_more) { // Poll again with afterCursor: events.next_cursor. } const pkg = await klarefi.cases.getPackage(session.session_id); ``` The operator and workflow surface is also available in the SDK: ```ts // cases:read const workspace = await klarefi.cases.getWorkspace(session.session_id); // cases:review. The SDK generates an idempotency key when omitted. // API keys cannot complete or reopen cases. const command = await klarefi.cases.review(session.session_id, { command_type: "approve_fact", payload: { fact_id: "incident_date" }, }); // cases:read const result = await klarefi.cases.getCommand( session.session_id, command.command_id, ); // cases:review const queue = await klarefi.operator.listQueue({ limit: 25 }); // workflows:read and workflows:write const { workflows } = await klarefi.workflows.list(); const draft = await klarefi.workflows.saveDraft(workflowDeclaration); // privacy:erase const receipt = await klarefi.documents.erase("doc_def456"); ``` `workflows.saveDraft()` validates and saves a draft. It cannot publish or activate a workflow; publishing remains human-controlled. CLI equivalents: ```bash npx klarefi cases get 550e8400-e29b-41d4-a716-446655440000 npx klarefi cases events 550e8400-e29b-41d4-a716-446655440000 npx klarefi cases package 550e8400-e29b-41d4-a716-446655440000 ``` ## Webhook verification Scaffold a receiver: ```bash npx klarefi webhooks init --framework next ``` Verify signatures in code with the raw request body: ```ts import { constructEvent, KlarefiWebhookSignatureError } from "@klarefi/node"; export async function POST(request: Request) { const rawBody = await request.text(); try { const event = constructEvent( rawBody, request.headers.get("X-Klarefi-Signature"), process.env.KLAREFI_WEBHOOK_SECRET!, ); if (event.event_type === "v1.case.completed") { // Read the case package and update your system of record. } return Response.json({ received: true }); } catch (error) { if (error instanceof KlarefiWebhookSignatureError) { return new Response("Invalid signature", { status: 401 }); } throw error; } } ``` Send a signed test delivery: ```bash npx klarefi webhooks test \ --endpoint-url https://your-app.example.com/webhooks/klarefi \ --signing-secret whsec_your_signing_secret ``` Verify a captured payload locally: ```bash npx klarefi webhooks verify \ --payload payload.json \ --signature "t=1704067200,v1=..." \ --secret whsec_your_signing_secret ``` ## Connectors Connector CLI commands shown below build, validate, and test local manifests; they do not install them. The public connector API and TypeScript SDK can create, import, list, inspect, and delete active remote connectors. Connector create/import calls go live immediately, so review functions and credentials before routing traffic. ```bash npx klarefi connectors init --name "Claims API" npx klarefi connectors infer --from . npx klarefi connectors validate klarefi.connector.json npx klarefi connectors test klarefi.connector.json \ --function lookup_record \ --input fixtures/connector-input.json ``` Use `--send` with `connectors test` only when the manifest points at a safe test endpoint. ## Errors Non-2xx API responses throw `KlarefiApiError` with `status`, `code`, `type`, and `requestId`. Network failures throw `KlarefiConnectionError`. ## Use with AI Agents can read the docs as plain text: ```bash export KLAREFI_DOCS_URL="https://www.klarefi.com" curl "$KLAREFI_DOCS_URL/llms.txt" curl "$KLAREFI_DOCS_URL/llms-full.txt" curl "$KLAREFI_DOCS_URL/docs-source/api/quickstart" ``` --- URL: https://www.klarefi.com/docs/api/sessions Markdown: https://www.klarefi.com/docs-source/api/sessions # Sessions API Create hosted intake sessions and redirect applicants to signed Klarefi intake URLs Source: https://www.klarefi.com/docs/api/sessions Use sessions for Hosted Intake integrations where Klarefi owns the hosted applicant intake flow. > **Use your workspace API base URL** > > Set `KLAREFI_API_BASE_URL` to the value in **Settings → Developer** before > using the examples. Current workspaces may use a > `https://.convex.site` URL. ## POST /api/v1/sessions Required scope: `intake:sessions:create` ```bash curl -X POST "$KLAREFI_API_BASE_URL/api/v1/sessions" \ -H "Authorization: Bearer $KLAREFI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "case_type_id": "motor_claim", "idempotency_key": "session_claim_12345", "external_case_id": "claim_12345", "external_applicant_id": "applicant_789", "external_customer_id": "customer_456", "trace_id": "trace_claim_12345", "ttl_hours": 168, "return_url": "https://claims.example.com/claim_12345", "locale": "nl", "prefill": { "field_values": { "claimant_name": "Ada Lovelace", "prior_reference": null } } }' ``` ## Request Body | Field | Required | Description | | ----------------------- | -------- | -------------------------------------------------- | | `case_type_id` | Yes | Case type configured in Klarefi | | `idempotency_key` | Yes | Retry-safe key, unless sent as a header | | `ttl_hours` | No | Link lifetime in hours, default `168` | | `return_url` | No | Destination after the applicant completes intake | | `locale` | No | BCP 47 locale hint for hosted intake | | `external_case_id` | No | Your source-system case identifier | | `external_applicant_id` | No | Your applicant identifier | | `external_customer_id` | No | Your customer or account identifier | | `trace_id` | No | Correlation ID included in events and reads | | `prefill.field_values` | No | Form field IDs mapped to `string` or `null` values | Prefill keys must identify fields in the configured form. Unknown fields reject the request. Compliance and consent fields cannot be prefilled. Klarefi applies accepted values to the session draft and returns their IDs in `prefill.applied_field_ids`. ## Response `201` means a new session was created. `200` means an idempotent replay. Test API keys can create up to 50 new sessions per workspace per UTC calendar month. Idempotent replays do not count again. When the cap is reached, this endpoint returns `429 test_usage_cap_exceeded`. ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400-e29b-41d4-a716-446655440000?token=...", "access_token": "1751879000.9f2c4e...", "access_token_expires_at": "2026-01-22T10:30:00.000Z", "expires_at": "2026-01-22T10:30:00.000Z", "agent": { "api_base": "https://your-deployment.convex.site/api/v1", "manifest_url": "https://your-deployment.convex.site/api/v1/sessions/550e8400-e29b-41d4-a716-446655440000/manifest", "access_token": "1751879000.9f2c4e..." }, "snippet": "", "idempotent_replay": false, "prefill": { "applied_field_ids": ["claimant_name", "prior_reference"] } } ``` An idempotent replay returns the same session with `idempotent_replay: true`. Prefill values in the replay request are not applied again. When the replay includes them, the response contains `prefill.applied_field_ids: []` and `prefill_ignored_on_replay: true`. Redirect the applicant to `signed_url`. Do not construct session URLs in the browser. `snippet` is a redirect helper, not an embed: injecting it sends the browser to `signed_url`. To render an intake inside your own site, use the [embed script](/docs/guides/embed) instead. --- URL: https://www.klarefi.com/docs/api/webhooks Markdown: https://www.klarefi.com/docs-source/api/webhooks # Webhooks API Receive signed Klarefi events, send test deliveries, and acknowledge handoff delivery Source: https://www.klarefi.com/docs/api/webhooks Klarefi delivers signed events to webhook endpoints registered in the dashboard. Use webhooks as integration signals, then read the current case or package when your system needs canonical state. > **Use your workspace API base URL** > > Set `KLAREFI_API_BASE_URL` to the value in **Settings → Developer** before > using API examples. Current workspaces may use a > `https://.convex.site` URL. >R: POST signed event R->>R: verify X-Klarefi-Signature R->>R: dedupe by delivery_id R-->>K: 2xx acknowledgement R->>A: enqueue internal work A->>K: GET /api/v1/cases/{caseId} K-->>A: current case record `} /> `v1.webhook.test` is emitted only by the test endpoint. Subscription events use the catalog names in the dashboard, such as `v1.intake.submitted` and `v1.case.completed`. ## Event shape ```json { "event_id": "evt_01J7W9S4TSQ0T4SH43JYB9W5R8", "delivery_id": "whd_01J7W9S52C2ZK1XFY2GM7Z7SAK", "event_type": "v1.case.completed", "semantic_event_type": "case.completed", "schema_version": "v1", "created_at": "2026-05-01T12:34:56.000Z", "occurred_at": "2026-05-01T12:34:50.000Z", "org_id": "org_abc123", "case_id": "case_abc123", "session_id": null, "external_case_id": "claim_12345", "external_applicant_id": "applicant_789", "external_customer_id": "customer_456", "idempotency_key": "session_claim_12345", "trace_id": "trace_claim_12345", "data": { "case_status": "completed" } } ``` Deliveries are at least once. Process duplicates idempotently by `delivery_id`. ## Retry schedule Klarefi makes up to 5 delivery attempts. After a failed attempt, retries are scheduled after 60 seconds, 5 minutes, 30 minutes, and 2 hours. After the final failed attempt, the delivery is marked `failed`. Klarefi emits `webhook.delivery_failed`, delivered as catalog event `v1.webhook.delivery_failed` for matching webhook subscriptions, unless the failed delivery was already a delivery-failed notification. Klarefi also creates an integration notification. If a `case.completed` handoff delivery exhausts its attempts, the case handoff state is marked dispatch failed. ## Signature verification Each delivery includes: ```http X-Klarefi-Signature: t=1704067200,v1=abc123def456... ``` The signature is: ```text HMAC-SHA256(signing_secret, timestamp + "." + raw_body) ``` Use the raw request body before JSON parsing. Install the SDK: ```bash npm install @klarefi/node ``` ```ts import { constructEvent, KlarefiWebhookSignatureError } from "@klarefi/node"; export async function POST(request: Request) { const rawBody = await request.text(); try { const event = constructEvent( rawBody, request.headers.get("X-Klarefi-Signature"), process.env.KLAREFI_WEBHOOK_SECRET!, ); if (event.event_type === "v1.case.completed") { // Read the case package and update your system of record. } return Response.json({ received: true }); } catch (error) { if (error instanceof KlarefiWebhookSignatureError) { return new Response("Invalid signature", { status: 401 }); } throw error; } } ``` ## Test a webhook receiver Required scope: `webhooks:test` ```bash npx klarefi webhooks test \ --endpoint-url https://your-app.example.com/webhooks/klarefi \ --signing-secret whsec_your_signing_secret ``` Use this command when you already have `KLAREFI_API_KEY` configured. The HTTP equivalent is: ```bash curl -X POST "$KLAREFI_API_BASE_URL/api/v1/webhooks/test" \ -H "Authorization: Bearer $KLAREFI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "endpoint_url": "https://your-app.example.com/webhooks/klarefi", "signing_secret": "whsec_your_signing_secret" }' ``` ```json { "success": true, "status_code": 200 } ``` To verify a captured payload locally: ```bash npx klarefi webhooks verify \ --payload payload.json \ --signature "t=1704067200,v1=..." \ --secret whsec_your_signing_secret ``` ## Acknowledge a delivery Required scope: `webhooks:acknowledge` With an existing `Klarefi` client: ```ts await klarefi.webhooks.acknowledge("whd_123", { acknowledgement_id: "ack_claim_12345", }); ``` ```bash curl -X POST "$KLAREFI_API_BASE_URL/api/v1/webhooks/deliveries/whd_123/ack" \ -H "Authorization: Bearer $KLAREFI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "acknowledgement_id": "ack_claim_12345" }' ``` ```json { "acknowledged": true, "already_acknowledged": false, "delivery_id": "whd_123", "case_id": "case_abc123", "handoff_state": "acknowledged" } ``` Acknowledgement is useful when your system wants Klarefi to record that a decision-ready handoff has been received. --- URL: https://www.klarefi.com/docs/guides/agent-intake Markdown: https://www.klarefi.com/docs-source/guides/agent-intake # Agent intake Let an agent complete a hosted intake session over the applicant API, the TypeScript SDK, or MCP Source: https://www.klarefi.com/docs/guides/agent-intake ## Overview Hosted intake sessions are agent-operable. Any agent that holds a signed session link can read the session state, fill the form, answer follow-up questions, upload documents, and submit, using the same endpoints the hosted UI uses. > **Use the canonical or discovered URL** > > Customer-plane integrations should copy their API base URL from > **Settings → Developer**, or use `https://www.klarefi.com`. For applicant-plane discovery, > `/s/{sessionId}/agent.json` carries the correct API base URL for that session; > prefer the URL returned by discovery. Two kinds of agents fit this model: - The applicant's own agent, completing the intake on the applicant's behalf. - Your agent, completing the parts your systems already know before handing the session to the applicant. An agent can only complete the intake. It cannot approve, reject, or otherwise decide the case. Human review stays in Klarefi. ## Two trust planes Agent intake separates who integrates from who completes. | Plane | Credential | Surface | Used for | | --------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | Customer plane | `sk_live_` / `sk_test_` API key | `/api/v1/*` REST, `@klarefi/node` | Your backend or agents: create sessions, prefill known values, read cases, get the decision package | | Applicant plane | Per-session bearer token (the `token` query parameter of the signed session link) | Session endpoints under `/api/v1/sessions/{sessionId}/*` | Whoever completes the intake: the applicant's agent, or your agent before handoff | The applicant plane needs no API key. The token in the signed link is the credential, scoped to one session. Treat the link as scoped applicant access: anyone who holds it can act on that session until it expires or is revoked. ## The agent loop Applicant-plane agents run one loop: read state, act, wait, repeat until done. >K: GET /api/v1/sessions/{sessionId} K-->>Agent: state_pointer + view Agent->>K: POST .../draft or .../replies or .../uploads K-->>Agent: accepted, session enters processing Agent->>K: GET .../events?after_cursor=N K-->>Agent: gap.prompted (new question ready) Agent->>K: POST .../replies Agent->>K: POST .../submit K-->>Agent: submitted, phase moves toward done `} /> 1. **Read state.** `GET /api/v1/sessions/{sessionId}` returns the current phase, the active task, and submit readiness. 2. **Act.** Save draft form values, answer the active question, upload a document, or submit. 3. **Wait.** After a reply the session enters `processing` while facts are resolved. Poll the events endpoint or re-read state until the next question is ready. 4. Repeat until the phase is `done`. ## Read session state Authenticate every applicant-plane request with the session token as a bearer token: ```http GET /api/v1/sessions/550e8400-e29b-41d4-a716-446655440000 Authorization: Bearer 1751879000.9f2c4e... ``` ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "state_pointer": { "phase": "question", "active_task_id": "task_7f3a", "submit_eligible": false, "submit_blocking_reasons": ["unresolved_follow_up"], "events_cursor": 14 }, "view": { "session": { "...": "..." }, "applicant_projection": { "...": "..." }, "session_progress": { "...": "..." } } } ``` `state_pointer` is the small part an agent should branch on. `view` is the full hosted context: form blocks, draft values, open tasks with typed response fields, uploads, and progress. The phase drives the loop: | Phase | Meaning | | ------------ | --------------------------------------------------------------- | | `form` | Structured form fields are open. Save drafts, then submit. | | `processing` | Klarefi is resolving facts. Wait and poll. | | `question` | A follow-up question is active. Answer it or upload a document. | | `done` | No applicant input remains. The case moves to review. | | `failed` | The session cannot continue. Surface the state to a human. | ## Act on the session All action endpoints live under `/api/v1/sessions/{sessionId}` and use the same bearer token. ### Save draft form values ```http POST /api/v1/sessions/{sessionId}/draft Authorization: Bearer Content-Type: application/json { "client_mutation_id": "d3b1c9e0-5f4a-4b2e-9c1d-8a7f6e5d4c3b", "updated_fields": { "claimant_name": "Ada Lovelace", "claim_number": "CLM-123" } } ``` Drafts do not trigger fact resolution. Use them to fill the form incrementally, then submit. ### Answer the active question ```http POST /api/v1/sessions/{sessionId}/replies Authorization: Bearer Content-Type: application/json { "content": "The vehicle was parked at the time of the incident.", "active_task_id": "task_7f3a", "idempotency_key": "reply:task_7f3a:14" } ``` To answer several open tasks in one request, send `{ "replies": [ ... ] }` with the same fields per entry. ### Upload a document Uploads are three steps: initialize, transfer, complete. ```http POST /api/v1/sessions/{sessionId}/uploads Authorization: Bearer Content-Type: application/json { "upload_id": "b6f0a1d2-3c4e-4f5a-8b9c-0d1e2f3a4b5c", "filename": "policy-card.pdf", "mime_type": "application/pdf", "size_bytes": 482113, "active_task_id": "task_9c2b" } ``` The response contains `doc_id` and a short-lived `upload_url`. Send the file bytes there with an HTTP `PUT`, then confirm: ```http POST /api/v1/sessions/{sessionId}/uploads/{docId}/complete Authorization: Bearer Content-Type: application/json { "storage_id": "st_a1b2c3", "active_task_id": "task_9c2b" } ``` Uploads are limited to 25MB. Each upload task lists its accepted MIME types; the manifest carries the session-wide policy. ### Submit ```http POST /api/v1/sessions/{sessionId}/submit Authorization: Bearer Content-Type: application/json { "submission_id": "e8c7d6b5-a4f3-4e2d-9c1b-0a9f8e7d6c5b" } ``` If the session is not ready, the API responds `409` with the code `submit_blocked` and the list of `blocking_reasons`. Resolve them (answer the open question, provide the missing upload) and retry with the same `submission_id`. ## Wait for the next step After a reply or submit, the session enters `processing` while Klarefi resolves facts. Two equivalent ways to wait: - Poll `GET /api/v1/sessions/{sessionId}/events?after_cursor=N`. A `gap.prompted` event means a new question is ready. - Re-read `GET /api/v1/sessions/{sessionId}` and check `state_pointer.phase`. ```http GET /api/v1/sessions/{sessionId}/events?after_cursor=14 Authorization: Bearer ``` ```json { "events": [ { "event_id": "evt_5c1d", "event_type": "gap.prompted", "cursor": 15, "created_at": "2026-07-07T09:14:03Z", "payload": { "active_task_id": "task_7f3a", "task_ids": ["task_7f3a"] } } ], "next_cursor": 15, "has_more": false, "phase": "question" } ``` Pass `next_cursor` as the next `after_cursor`. Poll no faster than every 2 seconds, and respect `Retry-After` headers when the API sends them. ## Discover capabilities `GET /api/v1/sessions/{sessionId}/manifest` returns a machine-readable descriptor of what the session supports right now: ```json { "auth": { "scheme": "bearer", "scope": "session" }, "actions": [ { "name": "save_draft", "available": true }, { "name": "reply", "available": true }, { "name": "upload", "available": true }, { "name": "submit", "available": false, "reason": "unresolved_follow_up" } ], "uploads": { "accepted_mime_types": ["application/pdf", "image/png", "image/jpeg"], "max_file_size_bytes": 26214400 }, "polling": { "min_interval_ms": 2000 } } ``` Fetch the manifest once at the start of a run instead of hardcoding capabilities. Availability changes as the session progresses. ### Discover from a session link A web agent often starts from the signed session link itself, not from API documentation. Session pages expose a token-free pointer for that case: ```http GET /s/{sessionId}/agent.json ``` ```json { "schema_version": "hosted_session_agent_pointer.v1", "session_id": "550e8400-e29b-41d4-a716-446655440000", "api_base": "https://your-deployment.convex.site/api/v1", "manifest_url": "https://your-deployment.convex.site/api/v1/sessions/550e8400-e29b-41d4-a716-446655440000/manifest", "auth": { "scheme": "bearer", "token_source": "the token query parameter of the signed session URL" }, "docs_url": "https://www.klarefi.com/docs/guides/agent-intake" } ``` The session page also carries a `rel="alternate"` link tag pointing at the same pointer, so an agent that parses the HTML finds it without knowing the URL shape. The pointer carries no credentials. Its `api_base` and `manifest_url` are derived from the configured Convex deployment, so they remain correct even when the public vanity domain is unavailable. An agent landing on a session link should fetch the pointer, take the bearer token from the link's `token` query parameter, and then fetch the manifest with that token to learn what the session supports. ## Idempotency Every mutation is safe to retry: - Replies accept `idempotency_key`. The convention is `reply:{task_id}:{source_event_sequence}`, which makes a retry of the same answer to the same question a no-op. - Submit accepts `submission_id` and replays instead of double-submitting. - Drafts carry `client_mutation_id` and uploads carry `upload_id` for the same reason. ## SDK quickstart `@klarefi/node` ships an applicant-plane client that wraps the loop, idempotency keys, and upload steps: ```bash npm install @klarefi/node ``` ```ts import { IntakeSession } from "@klarefi/node/intake"; const intake = IntakeSession.fromSignedUrl( "https://app.klarefi.com/s/550e8400...?token=1751879000.9f2c4e...", ); let state = await intake.getState(); while (state.next_action.type !== "none") { switch (state.next_action.type) { case "fill_form": state = await intake.saveDraft({ claimant_name: "Ada Lovelace" }); break; case "answer": state = await intake.answer("The claim number is CLM-123."); break; case "upload": state = await intake.uploadDocument({ file: "./policy-card.pdf", mimeType: "application/pdf", }); break; case "wait": { const result = await intake.nextAction({ timeoutMs: 60_000 }); state = result.state; break; } } } ``` You can also construct the client from parts: ```ts const intake = new IntakeSession({ sessionId: "550e8400-e29b-41d4-a716-446655440000", accessToken: process.env.INTAKE_SESSION_TOKEN!, baseUrl: process.env.KLAREFI_BASE_URL!, clientName: "claims-copilot", }); ``` The methods map onto the loop: | Method | What it does | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `getState()` | Distilled `AgentIntakeState`: `phase`, a typed `next_action` (`fill_form`, `answer`, `upload`, `wait`, `none`), progress counts, and submit blockers | | `answer(text, { taskId? })` | Reply to the active question with free text | | `answerFields({ factId: value })` | Reply with typed field values; validates against each task's response fields | | `saveDraft(fields)` | Save form field values without submitting | | `uploadDocument({ file, mimeType? })` | Runs initialize, transfer, and complete; accepts a path, `Uint8Array`, or `Blob` | | `submit()` | Returns `{ submitted, blockers, state }`; a `409 submit_blocked` becomes `submitted: false` with the blocking reasons | | `nextAction({ timeoutMs })` | Polls until the next action is ready or the timeout elapses | | `getView()` | The raw hosted view when an agent needs the full escape hatch | `nextAction()` polls every 2 seconds by default and respects the mutation it follows: it waits until the session has actually moved past the version your last write produced. ## MCP server Klarefi hosts a stateless MCP `2026-07-28` endpoint. It exposes the same loop as MCP tools, so an agent runtime can drive an intake without custom code: ```text https://www.klarefi.com/mcp ``` Use [Connect MCP](https://install.apicommons.org/?server=https%3A%2F%2Fwww.klarefi.com%2F.well-known%2Fmcp%2Fserver.json) to select your client. The installer reads Klarefi's public server manifest. It does not put an API key in the link. When the client asks for the `Authorization` header, enter `Bearer sk_test_...` or `Bearer sk_live_...`. Omit it when you only need applicant tools. The signed intake link is the applicant credential. Use the local package only when the client cannot connect to a remote MCP URL: ```json { "mcpServers": { "klarefi": { "command": "bunx", "args": ["--bun", "@klarefi/mcp"], "env": { "KLAREFI_API_KEY": "sk_test_..." } } } } ``` The hosted MCP server keeps no protocol session. Include the signed intake `url`, or `session_id` with `access_token`, in each applicant tool call. Local stdio clients can call `open_intake` once and reuse it on that connection. | Tool | Plane | What it does | | ----------------------- | --------- | --------------------------------------------------------------------------------------------- | | `open_intake` | Applicant | Open a session from a signed link | | `get_intake_state` | Applicant | Read the distilled state and next action | | `answer_question` | Applicant | Answer the active question with free text | | `answer_fields` | Applicant | Answer with typed field values | | `upload_document` | Applicant | Upload a document to the active upload task | | `save_intake_draft` | Applicant | Save form field values | | `submit_intake` | Applicant | Submit; returns blockers if the session is not ready | | `wait_for_next_action` | Applicant | Poll until the next action is ready | | `create_intake_session` | Customer | Create a hosted session; requires `intake:sessions:create` | | `get_case` | Customer | Read the case projection; requires `cases:read` | | `list_operator_queue` | Customer | Read the bounded queue; requires `cases:review` | | `get_case_workspace` | Customer | Read the cited workspace; requires `cases:read` | | `get_case_package` | Customer | Read the package and signed case-file link; requires `cases:read` | | `submit_case_review` | Customer | Submit an auditable review command; requires `cases:review`; cannot complete or reopen a case | | `get_review_command` | Customer | Poll a review command; requires `cases:read` | | `list_workflows` | Customer | Read workflow declarations; requires `workflows:read` | | `save_workflow_draft` | Customer | Save a draft; requires `workflows:write`; cannot publish or activate it | The read tools do not mutate cases or workflows. `submit_case_review` is limited to the review command set. Workflow publishing remains human-controlled. ## Prefill and handoff The customer plane lets your systems complete the parts they already know before the applicant sees the session. `POST /api/v1/sessions` accepts `prefill.field_values` with form field values from your records. The applicant only completes what remains. Compliance and consent fields cannot be prefilled; the applicant must provide those. ```http POST /api/v1/sessions Authorization: Bearer sk_live_... Content-Type: application/json { "case_type_id": "motor_claim", "idempotency_key": "session_claim_48392", "external_case_id": "claim_48392", "prefill": { "field_values": { "claimant_name": "Ada Lovelace", "policy_number": "POL-8842" } } } ``` The response includes `signed_url`, an `access_token` for the applicant plane, and an `agent` block describing the session endpoints, so your own agent can act on the session directly. When your side is done, mark the handoff: ```http POST /api/v1/sessions/{sessionId}/handoff Authorization: Bearer sk_live_... Content-Type: application/json { "notify": { "email": "ada@example.com" } } ``` Handoff marks the customer-completed portion, returns a fresh signed link to send to the applicant, and emits a `v1.intake.handoff_ready` webhook so your delivery flow can pick it up. The webhook remains the default integration path. `notify` is optional. When present, Klarefi emails the applicant a continue link directly, so a handoff works even without your own delivery flow. Notification is fail-soft: if the email cannot be sent, the handoff still succeeds. The response reports what happened in a `notification` block: ```json { "notification": { "requested": true, "sent": true } } ``` `requested` reflects whether the request asked for a notification, `sent` whether Klarefi delivered one, and a `reason` field explains any `sent: false`. Every agent-submitted answer is recorded with an actor and an optional `client_name`, and shows up in the case file. Reviewers see who provided each answer: your agent, the applicant's agent, or the applicant. ## Related guides - [Create an intake session](/docs/guides/create-intake-session) - [Hosted intake](/docs/guides/hosted-intake) - [Webhook setup](/docs/guides/webhooks) - [SDKs and CLI](/docs/api/sdks) - [Idempotency](/docs/api/idempotency) --- URL: https://www.klarefi.com/docs/guides/create-intake-session Markdown: https://www.klarefi.com/docs-source/guides/create-intake-session # Create an Intake Session Create a hosted intake session and hand the applicant a signed Klarefi intake URL Source: https://www.klarefi.com/docs/guides/create-intake-session ## Overview An intake session is the applicant-facing entry point for one case. Create a session when your system knows which workflow should run and who needs to submit answers or documents. ## Inputs A session creation request should include: - Case type identifier from your Klarefi dashboard - Applicant or case reference from your system - Optional callback or return destination - Optional locale and trace ID for support/debugging ## Example Request Install the SDK: ```bash npm install @klarefi/node ``` Create the session from trusted server-side code: ```ts import { Klarefi } from "@klarefi/node"; const klarefi = new Klarefi({ apiKey: process.env.KLAREFI_API_KEY!, }); const session = await klarefi.sessions.create({ case_type_id: "motor_claim", idempotency_key: "session_claim_48392", external_case_id: "claim_48392", external_applicant_id: "applicant_789", return_url: "https://claims.example.com/claim_48392", locale: "nl", }); return Response.redirect(session.signed_url, 303); ``` For a CLI smoke test: ```bash npx klarefi sessions create \ --case-type motor_claim \ --external-case-id claim_48392 \ --external-applicant-id applicant_789 \ --return-url https://claims.example.com/claim_48392 \ --url-only ``` The same request over HTTP: ```http POST /api/v1/sessions Authorization: Bearer sk_live_your_api_key Content-Type: application/json ``` ```json { "case_type_id": "motor_claim", "idempotency_key": "session_claim_48392", "external_case_id": "claim_48392", "external_applicant_id": "applicant_789", "return_url": "https://claims.example.com/claim_48392", "locale": "nl" } ``` The response contains `signed_url`, which can be opened by the applicant. Store the Klarefi `session_id` alongside your own case identifier. In v1 hosted sessions, the `session_id` is also the case ID used by case reads. ## Operational Guidance Create sessions from trusted server-side code. Do not build session URLs in the browser. Treat session links as scoped applicant access, and expire or revoke them when the case no longer needs applicant input. ## Related Guides - [Hosted intake](/docs/guides/hosted-intake) - [Sessions API](/docs/api/sessions) - [Webhook setup](/docs/guides/webhooks) - [Sessions reference](/docs/reference/sessions) --- URL: https://www.klarefi.com/docs/guides/embed Markdown: https://www.klarefi.com/docs-source/guides/embed # Embed Render a Klarefi intake inside your own site with the embed script, inline or as a floating launcher Source: https://www.klarefi.com/docs/guides/embed The embed script renders your published intake link inside your own site. Applicants fill out the form, upload documents, and answer follow-up questions without leaving your page. Your branding, form styling, and logic all carry over because the embed renders the same hosted intake. Get your snippet from the dashboard: open a workflow, choose Share intake link, then Embed on your site. The snippet already contains your link slug and the correct script URL. ## Inline Renders the intake into an element on your page and auto-resizes to the content height. ```html
``` If `data-target` is omitted or does not match an element, the script creates a container right before its own script tag. ## Launcher Adds a floating button in the bottom-right corner that opens the intake in a panel. Escape and clicking outside close the panel. The panel keeps its state across open and close, so applicants never lose progress. ```html ``` Optional: `data-color="#0f172a"` recolors the launcher button to match your site. ## JavaScript API The script exposes `window.Klarefi`: | Method | Description | | -------------------- | ------------------------------------------------------- | | `Klarefi.open()` | Opens the launcher panel | | `Klarefi.close()` | Closes the launcher panel | | `Klarefi.on(ev, cb)` | Subscribes to an event, returns an unsubscribe function | ## Events The iframe posts namespaced messages to your page. The script verifies the message origin in both directions against the origin it was loaded from, and payloads never contain applicant data. | Event | Payload | When | | ------------------- | --------------- | --------------------------------------------- | | `klarefi:ready` | none | The intake finished booting inside the iframe | | `klarefi:resize` | `{ height }` | Content height changed; inline mode resizes | | `klarefi:submitted` | `{ sessionId }` | The applicant submitted the intake | Subscribe with the plain event name: ```js Klarefi.on("submitted", function (payload) { console.log("Intake submitted", payload.sessionId); }); ``` ## Single-page apps The script is idempotent. If your router injects it again on navigation, an intake that is already mounted stays untouched and no duplicate is created. ## Attribution and white label Embedded intakes show a small "Powered by Klarefi" link below the form. Organizations with the white label add-on never show attribution, in embeds or anywhere else. ## Not the sessions `snippet` field The `snippet` field returned by `POST /api/v1/sessions` is a redirect helper: it sends the browser to the hosted session page. It does not embed anything. Use the embed script on this page to render an intake inside your site, and `signed_url` when you want to redirect an applicant to a session you created through the [Sessions API](/docs/api/sessions). ## Browser support The embedded session uses a partitioned cookie (CHIPS) so it works with third-party cookie blocking in current Chrome, Edge, Firefox, and Safari 18.4 or later. On browsers that reject all third-party cookies, applicants can still use your hosted intake link directly. ## Security notes - Only `/i/*` and `/s/*` are frameable. Every other Klarefi route refuses to render inside an iframe. - Any site can currently frame your public intake link, the same way any site can link to it. A per-organization domain allowlist is planned so you can restrict embedding to your own domains. - If your site enforces a Content Security Policy, allow the Klarefi origin in `script-src` and `frame-src`. --- URL: https://www.klarefi.com/docs/guides/hosted-intake Markdown: https://www.klarefi.com/docs-source/guides/hosted-intake # Hosted Intake Build a secure hosted intake flow for forms, documents, follow-up, and review-ready case files Source: https://www.klarefi.com/docs/guides/hosted-intake ## Overview Hosted intake gives each applicant a signed session where they can complete the form, upload documents, and answer follow-up questions. The session keeps form answers, documents, chat history, extracted facts, and review status together. Use hosted intake when the applicant-facing experience needs to stay simple but the back-office review requires evidence, completeness checks, and audit trails. ## Recommended Flow 1. Create an intake session from trusted server-side code. 2. Redirect the applicant to the hosted session URL. 3. Let Klarefi collect answers, documents, and follow-up responses. 4. Subscribe to webhook events for session and case state changes. 5. Pull the final case file into your system of record when review is ready. ## Install ```bash npm install @klarefi/node export KLAREFI_API_KEY="sk_test_..." npx klarefi doctor ``` ## Create And Redirect ```ts import { Klarefi } from "@klarefi/node"; const klarefi = new Klarefi({ apiKey: process.env.KLAREFI_API_KEY!, }); const session = await klarefi.sessions.create({ case_type_id: "motor_claim", external_case_id: "claim_12345", external_applicant_id: "applicant_789", idempotency_key: "session_claim_12345", return_url: "https://claims.example.com/claim_12345", locale: "nl", }); return Response.redirect(session.signed_url, 303); ``` The CLI can scaffold starter files for common frameworks: ```bash npx klarefi hosted-intake install --framework next ``` ## Session State Hosted sessions progress through states such as created, submitted, blocked, needs review, and completed. Treat webhooks as notifications and read the current session or case state from the API when your system needs the latest record. ## Design Notes Hosted intake should ask only for what the case needs. Prefer targeted follow-up questions over generic resubmission messages. If a fact cannot be supported by evidence, route the case to clarification or human review instead of guessing. ## Related Guides - [Create an intake session](/docs/guides/create-intake-session) - [Upload documents](/docs/guides/upload-documents) - [Webhook setup](/docs/guides/webhooks) --- URL: https://www.klarefi.com/docs/guides/hosted-intake-quickstart Markdown: https://www.klarefi.com/docs-source/guides/hosted-intake-quickstart # Quickstart: Hosted Intake Create a hosted intake session, redirect the applicant, and read the resulting case file Source: https://www.klarefi.com/docs/guides/hosted-intake-quickstart ## Overview Use Hosted Intake when Klarefi should run the applicant-facing flow. Your backend creates a session, redirects the applicant to a signed URL, and then uses webhooks or case reads to consume the result. ## Typical Fit - You want a hosted applicant-facing intake flow. - You want Klarefi to manage document collection and follow-up questions. - You want operators to review the resulting case in Klarefi. ## Basic Flow 1. Create an intake session. 2. Redirect the applicant to the signed hosted URL. 3. Receive webhook events or poll case state. 4. Read the case or case package when it is ready for review. >K: POST /api/v1/sessions K-->>App: signed_url App-->>Applicant: redirect to signed_url Applicant->>K: complete form and upload documents K-->>App: signed webhook event App->>K: GET /api/v1/cases/{caseId} K-->>App: case with facts and evidence `} /> ## Create A Session Install the TypeScript SDK: ```bash npm install @klarefi/node ``` Create sessions from trusted server-side code: ```ts import { Klarefi } from "@klarefi/node"; const klarefi = new Klarefi({ apiKey: process.env.KLAREFI_API_KEY!, }); const session = await klarefi.sessions.create({ case_type_id: "motor_claim", idempotency_key: "session_2026_05_01_001", external_case_id: "claim_12345", external_applicant_id: "applicant_789", ttl_hours: 168, locale: "nl", }); ``` The same request over HTTP: ```http POST /api/v1/sessions Authorization: Bearer sk_live_... Content-Type: application/json { "case_type_id": "motor_claim", "idempotency_key": "session_2026_05_01_001", "external_case_id": "claim_12345", "external_applicant_id": "applicant_789", "ttl_hours": 168, "locale": "nl" } ``` Example response: ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "signed_url": "https://app.klarefi.com/s/550e8400...?token=...", "expires_at": "2026-01-22T10:30:00Z", "idempotent_replay": false } ``` ## Redirect The Applicant Send the applicant to the returned `signed_url`. From there, Klarefi owns the hosted intake flow, including structured sections, uploads, and follow-up questions. ```ts return Response.redirect(session.signed_url, 303); ``` For a quick local check: ```bash npx klarefi sessions create \ --case-type motor_claim \ --external-case-id claim_12345 \ --url-only ``` ## Consume The Result After the applicant submits the hosted intake, consume: - webhook events for state changes - case reads for current case state - case packages for review-ready handoff material The case is the result. Hosted Intake does not produce a separate output model. ```ts const caseFile = await klarefi.cases.retrieve(session.session_id); const pkg = await klarefi.cases.getPackage(session.session_id); ``` --- URL: https://www.klarefi.com/docs/guides/upload-documents Markdown: https://www.klarefi.com/docs-source/guides/upload-documents # Upload Documents Attach PDFs, scans, photos, and supporting documents to a hosted intake session Source: https://www.klarefi.com/docs/guides/upload-documents ## Overview In Hosted Intake, applicants upload documents inside the Klarefi session. Those documents stay attached to the case, so the resulting facts, gaps, and evidence all point back to the same intake record. ## Hosted Intake Uploads Create the hosted intake session, redirect the applicant to `signed_url`, and let Klarefi ask for the documents required by the workflow. The applicant does not need a separate upload portal. ## Document Metadata Use stable identifiers on the session when you already know them: - `external_case_id` - `external_applicant_id` - `external_customer_id` - `trace_id` These identifiers let your system reconcile webhook events, case reads, and the final case package back to your own records. ## Review Behavior Uploaded documents are read against the workflow's required facts. Klarefi can continue the intake when evidence is present, ask a follow-up question when something is missing, or route the case to review when the document is ambiguous or unreadable. ## Related Guides - [Hosted intake](/docs/guides/hosted-intake) - [Create an intake session](/docs/guides/create-intake-session) - [Webhook setup](/docs/guides/webhooks) - [Evidence reference](/docs/reference/evidence) --- URL: https://www.klarefi.com/docs/guides/webhooks Markdown: https://www.klarefi.com/docs-source/guides/webhooks # Webhook Setup Receive signed Klarefi events and verify webhook signatures Source: https://www.klarefi.com/docs/guides/webhooks ## Overview Klarefi sends signed, versioned domain events for intake, case, review, document, and webhook delivery activity. Use webhooks to learn when a Hosted Intake case changes, then read the current case from the API. > **Deployment gate** > > Self-serve webhook registration and delivery require > `KLAREFI_ENABLE_SELF_SERVE_WEBHOOKS` to be enabled for the deployment. When > it is disabled, self-serve intake creation returns > `428 self_serve_webhooks_disabled`. > **Use your workspace API base URL** > > Copy it from **Settings → Developer** before using the API example below. > Current workspaces may use a `https://.convex.site` URL. All webhook deliveries are sent to registered endpoints and signed with HMAC-SHA256. Per-request webhook URLs are rejected; configure endpoint URLs in the dashboard so delivery policy, retries, and signing secrets stay consistent. ## Setting Up Webhooks ### 1. Register an Endpoint Register a webhook endpoint in the Klarefi dashboard under **Webhooks**. You'll receive an endpoint signing secret (`whsec_...`) to verify incoming events. Secrets are scoped to a registered endpoint and are shown only once. ### 2. Implement Your Receiver Your endpoint must: - Accept POST requests with JSON body - Return a 2xx status code within 30 seconds - Verify the signature before processing - Process duplicate deliveries idempotently using the stable `delivery_id` For a Next.js starter route: ```bash npx klarefi webhooks init --framework next ``` ## Signature Verification Every webhook includes an `X-Klarefi-Signature` header: ``` X-Klarefi-Signature: t=1704067200,v1=abc123def456... ``` The signature is computed as: `HMAC-SHA256(signing_secret, timestamp + "." + raw_body)` ## Event Payloads Events use a versioned name and carry schema metadata. The event name is stable within a version; additive fields may appear over time, so ignore unknown keys. ```json { "event_id": "evt_01J7W9S4TSQ0T4SH43JYB9W5R8", "delivery_id": "whd_01J7W9S52C2ZK1XFY2GM7Z7SAK", "event_type": "v1.case.completed", "schema_version": "v1", "trace_id": "trc_01J7W9S4V8ZKH4W4B8EG9J7TKC", "created_at": "2026-05-01T12:34:56.000Z", "data": { "case_status": "completed" } } ``` `delivery_id` is stable for a delivery attempt series and should be used for idempotency. `event_id` identifies the domain event. `trace_id` can be used when correlating dashboard delivery logs with receiver logs. ## Event Catalog Webhook endpoints can subscribe to individual event types or `*` for the current catalog: - `v1.intake.session_created` - `v1.intake.session_expired` - `v1.intake.handoff_ready` - emitted when the customer marks hosted intake handoff complete - `v1.intake.submitted` - `v1.case.processing` - `v1.case.applicant_blocked` - `v1.case.needs_review` - `v1.case.completed` - `v1.case.failed` - `v1.review.completed` - `v1.document.processed` - `v1.document.failed` - `v1.intake.gap_resolved` - `v1.fact.evidence_updated` - emitted when an intake-review command updates a fact's evidence - `v1.webhook.delivery_failed` - `*` The API test endpoint sends a signed `v1.webhook.test` payload. It is not a subscription event; use it only to verify receiver parsing and signatures. ### TypeScript ```bash npm install @klarefi/node ``` ```typescript import { constructEvent, KlarefiWebhookSignatureError } from "@klarefi/node"; import express from "express"; const app = express(); app.post( "/webhooks/klarefi", express.raw({ type: "application/json" }), async (req, res) => { try { const event = constructEvent( req.body.toString(), req.headers["x-klarefi-signature"] as string, "whsec_your_signing_secret", ); console.log("Received event:", event.event_type, event.delivery_id); res.sendStatus(200); } catch (error) { if (error instanceof KlarefiWebhookSignatureError) { res.sendStatus(401); return; } console.error("Webhook handling failed:", error); res.sendStatus(500); } }, ); ``` ### Python ```python import hashlib import hmac import json import time from flask import Flask, request app = Flask(__name__) def verify_webhook_signature(raw_body: bytes, signature_header: str, signing_secret: str) -> dict: parts = dict(part.split("=", 1) for part in signature_header.split(",") if "=" in part) timestamp = int(parts["t"]) if abs(int(time.time()) - timestamp) > 300: raise ValueError("stale signature") expected = hmac.new( signing_secret.encode(), str(timestamp).encode() + b"." + raw_body, hashlib.sha256, ).hexdigest() if not hmac.compare_digest(expected, parts["v1"]): raise ValueError("invalid signature") return json.loads(raw_body) @app.route("/webhooks/klarefi", methods=["POST"]) def handle_webhook(): try: event = verify_webhook_signature( raw_body=request.get_data(), signature_header=request.headers.get("X-Klarefi-Signature"), signing_secret="whsec_your_signing_secret", ) if event["event_type"] == "v1.case.completed": print("Case completed:", event["case_id"]) # Read the current case or package from the API. return "", 200 except Exception as e: print("Webhook verification failed:", e) return "", 401 ``` ## Event Handling Guidance Treat webhook events as integration signals, then read the current case or session state from the API when you need the latest result. This avoids coupling your integration to a long list of event-specific assumptions. ## Testing Webhooks Send a test event to verify your receiver works before going live: ```bash npx klarefi webhooks test \ --endpoint-url https://your-app.com/webhooks/klarefi \ --signing-secret whsec_your_signing_secret ``` To verify a captured payload locally: ```bash npx klarefi webhooks verify \ --payload payload.json \ --signature "t=1704067200,v1=..." \ --secret whsec_your_signing_secret ``` The same test delivery over HTTP: ```bash curl -X POST "$KLAREFI_API_BASE_URL/api/v1/webhooks/test" \ -H "Authorization: Bearer sk_live_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "endpoint_url": "https://your-app.com/webhooks/klarefi", "signing_secret": "whsec_your_signing_secret" }' ``` ## Retry Policy If delivery fails, Klarefi retries with backoff and marks the delivery as `pending`, `delivering`, `retrying`, `delivered`, or `failed` in the dashboard. Exhausted deliveries are moved to the dead-letter queue and can emit `v1.webhook.delivery_failed` to subscribed endpoints. Use the dashboard delivery logs, `delivery_id`, and `trace_id` to debug failures or replay failed deliveries. --- URL: https://www.klarefi.com/docs/reference/cases Markdown: https://www.klarefi.com/docs-source/reference/cases # Cases Case objects, statuses, and review states for Klarefi intake workflows Source: https://www.klarefi.com/docs/reference/cases ## Case Object A case represents one intake packet moving from submission to review. It keeps the applicant session, documents, extracted facts, follow-up history, review state, and integration metadata together. ## Common Fields - `case_id`: Klarefi case identifier - `case_type_id`: Case type used to evaluate the intake - `workflow_id` and `workflow_version`: Workflow declaration used by the case - `operating_mode`: `system_of_intelligence` or `system_of_action` - `runtime_status`: `processing`, `intake_in_progress`, `gate_ready`, `gate_evaluating`, `ready_for_review`, or `complete` - `workspace.facts`: Extracted facts with resolution status and cited evidence - `workspace.documents`: Documents associated with the case - `workspace.operational_summary`: Blockers, ownership, and the primary action - `workspace.summary`: Fact and document counts - `updated_at`: Projection update time ## Status Guidance Use `runtime_status` for workflow orchestration. Use the queue projection for operator queues and `workspace.facts[].status` for item-level resolution state. --- URL: https://www.klarefi.com/docs/reference/events Markdown: https://www.klarefi.com/docs-source/reference/events # Events Klarefi event names and webhook delivery guidance for intake integrations Source: https://www.klarefi.com/docs/reference/events ## Event model Events are versioned integration signals. Use them to wake up your system, then read the canonical session or case state when you need current data. ## Common events - `v1.intake.session_created` - `v1.intake.session_expired` - `v1.intake.submitted` - `v1.case.processing` - `v1.case.applicant_blocked` - `v1.case.needs_review` - `v1.case.completed` - `v1.case.failed` - `v1.review.completed` - `v1.document.processed` - `v1.document.failed` - `v1.intake.gap_resolved` - emitted when a missing information request is resolved - `v1.fact.evidence_updated` - `v1.webhook.delivery_failed` `fact.review_completed` maps to catalog event `v1.review.completed` for historical compatibility. Webhook payloads still carry the fact event stem in `semantic_event_type`. `v1.webhook.test` is reserved for the webhook test endpoint and is not a subscription event. ## Delivery guidance Process duplicate deliveries idempotently using `delivery_id`. Use `trace_id` to correlate webhook delivery logs with your own receiver logs. --- URL: https://www.klarefi.com/docs/reference/evidence Markdown: https://www.klarefi.com/docs-source/reference/evidence # Evidence Evidence objects, source citations, and review metadata for extracted facts Source: https://www.klarefi.com/docs/reference/evidence ## Evidence Object Evidence connects an extracted fact to the source that supports it. Evidence is what makes AI output reviewable. ## Common Fields - `evidence_id`: Evidence identifier - `fact_id`: Fact the evidence supports - `document_id`: Source document identifier - `quote`: Source quote or extracted span when available - `page`: Page or location reference - `review_state`: Accepted, disputed, missing, or review-only ## Review Guidance Treat evidence as review material, not a final decision. If a value cannot be supported by source evidence, keep the fact unresolved and route the case to clarify or human review. --- URL: https://www.klarefi.com/docs/reference/sessions Markdown: https://www.klarefi.com/docs-source/reference/sessions # Sessions Hosted intake session fields, lifecycle states, and integration guidance Source: https://www.klarefi.com/docs/reference/sessions ## Session Object A session is the applicant-facing intake container. It stores the signed hosted URL, case type, linked case, answer state, document state, and submission status. ## Common Fields - `session_id`: Klarefi session identifier - `signed_url`: Applicant URL returned by `POST /api/v1/sessions` - `case_type_id`: Case type assigned to the session - `case_id`: Linked case identifier, currently the same value as `session_id` - `external_case_id`: Your source-system reference - `status`: Session lifecycle state - `expires_at`: Session expiration timestamp - `trace_id`: Optional integration correlation ID ## Lifecycle Create a session from trusted server-side code. Redirect the applicant to the signed URL. Subscribe to webhook events for state changes, and read current state from the API when you need the latest record.