# 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": "<script>window.location.href=\"https://app.klarefi.com/s/...\";</script>",
  "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"
}
```
