# Uploads and processing

Upload a record, start AI extraction, poll for the result, review it and confirm. Includes credits and what happens when processing stalls.


Turning an image into people and facts takes five steps: upload, process, poll, review, confirm. Processing is asynchronous: `POST .../records/{record_id}/process` returns `202`, then you poll `GET .../records/{record_id}` until `status` is `review` or `failed`.

## 1. Upload

Upload a JPG or PNG (up to 10 MB) as multipart form data:

```bash
curl -X POST https://kleiobase.com/api/v1/workspaces/WORKSPACE_ID/records \
  -H "Authorization: Bearer kb_live_YOUR_KEY" \
  -F "file=@birth-record.jpg"
```

You get `201` with the record in status `pending`. Optional fields `source_text` and `source_text_layout` let you send a text transcription along with the image.

Through the [MCP server](/developers/docs/mcp), the same upload also works from an https URL, from a short-lived upload link that a script fills with `curl --upload-file` (or a person fills in the browser), or as base64 content. All of them take the same JPG or PNG images and end in this endpoint.

**Uploads are not idempotent.** Retrying creates another record, and `Idempotency-Key` is ignored on this endpoint. If an upload times out, list your records before retrying.

## 2. Process

```bash
curl -X POST https://kleiobase.com/api/v1/workspaces/WORKSPACE_ID/records/RECORD_ID/process \
  -H "Authorization: Bearer kb_live_YOUR_KEY" \
  -H "Idempotency-Key: process-record-1" \
  -H "Content-Type: application/json" \
  -d '{"deep": false}'
```

Processing is asynchronous. The call returns `202` as soon as credits are charged, and the extraction runs in the background.

### Credits

- A normal extraction costs 1 credit.
- A deep scan (`"deep": true`) costs 5 credits.
- If you do not have enough, you get `402 insufficient_credits`, with `used`, `limit`, `credits_remaining` and `shortfall` in `details`.
- Too many records processing at once returns `429`.

Credits are your account's monthly credits, shared with the KleioBase app.

## 3. Poll

```bash
curl https://kleiobase.com/api/v1/workspaces/WORKSPACE_ID/records/RECORD_ID \
  -H "Authorization: Bearer kb_live_YOUR_KEY"
```

Check every few seconds until `status` is `review` (ready for you) or `failed`.

## 4. Review and edit

Read the extraction:

```bash
curl https://kleiobase.com/api/v1/workspaces/WORKSPACE_ID/records/RECORD_ID/extraction \
  -H "Authorization: Bearer kb_live_YOUR_KEY"
```

To correct it, send the whole edited extraction with `PATCH` to the same URL. Nothing is created until you confirm. Dates are corrected here, in the extraction. `PATCH .../records/{id}` only changes the record's title and type, and it returns `409` while the record is processing.

## 5. Confirm

```bash
curl -X POST https://kleiobase.com/api/v1/workspaces/WORKSPACE_ID/records/RECORD_ID/confirm \
  -H "Authorization: Bearer kb_live_YOUR_KEY" \
  -H "Idempotency-Key: confirm-record-1"
```

This creates the people, links and families from the extraction. If it would exceed your plan's person limit you get `403 forbidden`. Without an `Idempotency-Key`, retrying after success returns `409` because the record is already confirmed.

## When processing stalls

If a run crashes or never finishes, KleioBase recovers it automatically. A cleanup job runs every 5 minutes and handles any run older than 30 minutes, so allow about 35 minutes:

- The credits are refunded.
- The record goes to `failed`, or back to `review` if it already had an extraction (for example an interrupted re-scan).

Until then, calling process again can return `409` "already being processed". That can last up to about 35 minutes after a crashed run. Keep polling the record rather than retrying in a loop.

## Rate limits

An upload counts against both your request budget and the smaller upload budget. See [Rate limits](/developers/docs/rate-limits).

Canonical: https://kleiobase.com/developers/docs/uploads
