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, or for an unparseable value, 409 stale_write and nothing changes. 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. On the hosted service 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.

{
"id": "mem_8f2c1a",
"content": "Giulia now prefers in-person standups.",
"user_id": "customer-giulia-4812",
"agent_id": "onboarding-bot",
"run_id": null,
"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).
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). On the hosted service 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.
503writes_pausedOnly when extraction would run inside the call: the service's daily model budget is spent, nothing changed, retry after Retry-After.
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.
422invalid_requestcontent is empty or whitespace-only, or its length is outside 1-16,000 characters; 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 value is treated as stale, so the write is rejected rather than allowed to clobber.
  • 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