Batch & status
Batch import
Bulk-import up to 500 memories in one async call. Korely enqueues the job and returns a job id immediately; each item is imported later through the same pipeline as a single add.
/v1/batch
Use this when you're backfilling history or seeding a namespace and don't
want to fire hundreds of individual POST /v1/memories calls. The
request returns 202 Accepted with a job id; a background worker
imports each item, running embeddings, typed-fact extraction, and
contradiction checking, exactly as a single add would. Poll
GET /v1/batch/{job_id} for progress.
Authentication
HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:write scope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
memories | array<object> | Required | The memory objects to import. 1-500 items. Each element is a BatchMemory (see fields below). |
Each BatchMemory element has these fields:
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Required | The memory text. 1-16,000 characters. |
user_id | string · null | Optional | The end-user namespace this memory belongs to. Default null. Max 255 chars. |
agent_id | string · null | Optional | Your application's namespace. Default null. Max 255 chars. |
run_id | string · null | Optional | One session or conversation. Default null. Max 255 chars. |
metadata | object · null | Optional | Arbitrary JSON stored alongside the memory and echoed back on reads. Default null. |
Example request
curl -X POST https://api.korely.ai/v1/batch \ -H "Authorization: Bearer kor_live_..." \ -H "Content-Type: application/json" \ -d '{ "memories": [ { "content": "User prefers email follow-ups, not phone", "user_id": "customer-giulia-4812", "agent_id": "support-bot" }, { "content": "Account is on the EU data region", "user_id": "customer-giulia-4812", "agent_id": "support-bot" }, { "content": "Renewal date is 2026-09-01", "user_id": "customer-giulia-4812", "agent_id": "support-bot", "metadata": { "source": "crm" } } ] }'Response
202 Accepted. The job has been enqueued; nothing is imported
yet. Poll GET /v1/batch/{job_id} to track progress.
{ "id": "job_8f2c1aab4d7e4f0c9a1b2c3d4e5f6a7b", "status": "processing", "received": 3}| Field | Type | Description |
|---|---|---|
id | string | Opaque batch job id, prefix job_ followed by the UUID hex, e.g. job_8f2c1aab4d7e4f0c9a1b2c3d4e5f6a7b. Use it to poll GET /v1/batch/{job_id}. |
status | string | Always processing in the POST response. (The persisted job starts in pending and advances as the worker drains it.) |
received | integer | Number of memory objects accepted into the job, equals the length of the memories array you sent. |
Errors
| Status | Code | Cause |
|---|---|---|
401 | invalid_key | Missing or invalid API key. No Bearer credential was supplied, or the kor_live_ key does not resolve to a live key. |
403 | forbidden | The API key lacks the memories:write scope. |
422 | invalid_request | Request validation failed, memories is empty or has more than 500 items, a content is empty or over 16,000 chars, or a user_id / agent_id / run_id exceeds 255 chars. |
429 | rate_limit_exceeded | Per-tier fixed-window request rate limit hit. Comes back with a Retry-After header. This is the per-minute / hour / day request limit, distinct from the monthly write quota, which is enforced per item inside the worker, not at POST time. |
Notes
- It's asynchronous. The POST only enqueues the job and returns immediately (
202). A background worker imports each item later through the same pipeline asPOST /v1/memories,engine.addplus best-effort typed-fact extraction. - Each item counts as one write. Every imported item is checked against the per-agent namespace cap first, then your monthly write quota, so a capped item never burns a quota count.
- Quota mid-batch fails gracefully. Once the monthly write quota is hit, the remaining items fail individually with a quota error (no silent overage) and are recorded in the job's
errorsarray. They do not429the original POST. - Webhooks fire per item. The worker emits a
memory.createdwebhook for each successful import, and a one-timequota.warningwebhook at exactly 80% usage. - Crash-safe and idempotent. The worker claims jobs atomically (
SELECT ... FOR UPDATE SKIP LOCKED), uses bounded attempts with exponential backoff, and resumes from theimported + failedoffset after a crash, so a restart never double-imports. - Hard ceiling. A single batch accepts at most 500 items.
Related
- Batch status, poll
GET /v1/batch/{job_id}for progress and per-item errors. - Add a memory, the single-item write each batch item runs through.
- SDK, the typed client surface.