Korely

Core memory operations

Update a memory

Replace a memory's content. Korely re-runs fact extraction on the new text: changed values supersede prior facts, restated facts dedupe, and facts the new text no longer states are closed, so your typed knowledge stays in sync.

PATCH /v1/memories/{memory_id}

SDK: korely.update(memory_id, content=...). The update path re-runs the intelligence, embeddings and typed-fact extraction with bi-temporal contradiction checking, on the new content. Facts the new text restates stay active (deduped); facts it contradicts are superseded; facts it no longer states are closed at the time of the edit, unless another memory also states them. You never write supersede logic.

Authentication

HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:write scope.

Path parameter

ParameterTypeRequiredDescription
memory_idstringRequiredThe public memory id (mem_) to update. A malformed id returns 404.

Request body

FieldTypeRequiredDescription
contentstringRequiredNew content. 1-16,000 characters. Stripped and rejected if blank or whitespace-only (422).
expected_updated_atstringOptionalOptimistic concurrency token: the updated_at you last read, or created_at if updated_at is null, sent back exactly as the API returned it. The edit is written only if the memory is still at that version, to the microsecond, when it is written; otherwise 409 stale_write and nothing changes. A value that is not an ISO 8601 instant, the empty string included, is a 422. Leave the field out for an edit without the check. Defaults to null (no concurrency check).

Any other field is refused with 422 invalid_request (a memory's user_id, agent_id, run_id and metadata do not change with an edit). Field names are snake_case.

Example request

Terminal window
curl -X PATCH https://api.korely.ai/v1/memories/mem_8f2c1a \
-H "Authorization: Bearer kor_live_..." \
-H "Content-Type: application/json" \
-d '{
"content": "Giulia now prefers in-person standups.",
"expected_updated_at": "2026-03-01T09:14:22.418093+00:00"
}'

Response

200 OK. The updated memory. By default (WRITE_ASYNC=true, on the Cloud and on Self-hosted) extraction runs after the call returns, so the response carries status processing and an empty facts list; the facts re-extracted from the new content follow a few seconds later. A Self-hosted install with WRITE_ASYNC=false extracts inside the call and returns them.

{
"id": "mem_8f2c1a",
"content": "Giulia now prefers in-person standups.",
"user_id": "customer-giulia-4812",
"agent_id": "onboarding-bot",
"run_id": null,
"client": null,
"kind": "note",
"metadata": {"source": "slack"},
"status": "processing",
"created_at": "2026-03-01T09:14:22.418093+00:00",
"updated_at": "2026-03-02T11:02:10.183112+00:00",
"facts": []
}
FieldTypeDescription
idstringThe public memory id (mem_).
contentstringThe updated content, echoed back.
user_idstring · nullThe end user this memory belongs to (null if not scoped).
agent_idstring · nullThe agent namespace (null if not scoped).
run_idstring · nullThe run namespace (null if not scoped).
clientstring · nullThe tool that wrote it, from the X-Korely-Client header of the write or the name an MCP client gave when it connected; a label, not a namespace.
kindstringnote, or session for the log of a work session (the kind of the write).
metadataobjectThe metadata dict stored with the memory.
created_atstring · nullISO 8601 timestamp.
updated_atstring · nullISO 8601 timestamp.
statusstringprocessing while the new facts are being extracted, then ready (or error). By default extraction runs after the call returns, as on POST /v1/memories: the edit answers processing and the facts follow.
factsarrayTyped (subject, predicate, object) facts re-extracted from the new content, when extraction ran inside the call, in the write shape of Add a memory; empty while status is processing (read them later with Get memory or Get facts). Changed values supersede prior facts via the contradiction pipeline; restated facts are deduped.

Errors

StatusCodeCause
401invalid_keyMissing or invalid kor_live_ key, Invalid or missing API key: ..., then what is wrong.
429quota_exceededAn edit extracts again and counts the writes of its new text (one per 6,000 characters): past the monthly write quota it is refused and nothing changes.
403forbiddenKey lacks the memories:write scope, API key missing required scope(s): memories:write.
404not_foundMalformed memory_id, or no such memory in this key's project (including when checked against expected_updated_at), Memory not found.
409stale_writeexpected_updated_at was provided but doesn't match the current record, expected_updated_at does not match the current record.
413payload_too_largeThe body is larger than 2,097,152 bytes. Refused before it is read; nothing changes. On the Cloud a body over 1 MB gets a bare 413 first, without this code.
422invalid_requestcontent is empty or whitespace-only, or its length is outside 1-16,000 characters; or expected_updated_at is not an ISO 8601 instant; or the body has a field other than content and expected_updated_at.
429rate_limit_exceededRate limit exceeded, returned with a Retry-After header.

Notes

  • It's a write. Update requires the memories:write scope and does not meter the read query quota.
  • Facts stay in sync. The PATCH re-runs fact extraction on the new content: changed values supersede prior facts (their invalid_at is set), restated facts are deduped, and a fact the new text no longer states is closed at the time of the edit, unless another memory also states it.
  • Optimistic concurrency. expected_updated_at is compared to the microsecond, with no tolerance: two edits made from the same read, even in the same second, cannot both win; the second gets 409 stale_write. The check holds at the write itself, so an edit that lands between your request's check and its write is not overwritten either. Send back the string you read: a value rounded to the millisecond (a JavaScript Date, for example) is another version. An unparseable or empty value is refused with 422 invalid_request, and nothing changes.
  • Webhooks. The PATCH emits no memory.created. When re-extraction runs, each fact it closes fires fact.invalidated: invalidated_by is the new fact, or null with reason: "memory_updated" for a fact the new text no longer states.

Related