Core operations
Update a memory
Replace a memory's content and re-extract its facts in one call.
When you send a PATCH request, Korely replaces the stored content,
then runs the full extraction pipeline on the new text: entity detection,
typed fact extraction, and the two-stage contradiction check. Facts the new
text restates stay active; facts it contradicts are superseded by the incoming
facts; facts it no longer states are closed at the time of the edit, unless
another memory also states them. On the hosted service this runs right after the call returns:
the response carries status: "processing" and an empty
facts list, and the new facts land a few seconds later. An
update counts against your monthly write quota like an add, one write per
6,000 characters of the new text, because it runs extraction again.
Call this whenever an agent learns that previously recorded information is wrong or outdated, for example, a customer corrects a preference mid-session, or a support case is re-categorised after triage. You do not need to delete the memory and re-add it: update it in place and Korely handles the contradiction resolution automatically.
flowchart LR
A([PATCH /v1/memories/mem_8f2c1a]) --> B[Replace content]
B --> C[Re-run extraction]
C --> D{Contradiction check}
D -->|supersedes old fact| E[Invalidate fct_b91e]
D -->|no conflict| F[Keep existing facts]
E --> G([Facts current, status ready])
F --> G Request
Endpoint: PATCH /v1/memories/{id}. SDK: korely.update(memory_id, *, content, expected_updated_at=None).
| Param | Type | Notes |
|---|---|---|
id | string | Required. The memory ID in the URL path (e.g. mem_8f2c1a). |
content | string | Required. The new plain text or Markdown that fully replaces the existing content. Extraction runs on this text. |
expected_updated_at | ISO 8601 string | Optional. Optimistic concurrency guard. Pass the updated_at value you last read, or created_at if updated_at is null, exactly as the API returned it. Compared to the microsecond, at the write itself: a different or unparseable value returns 409 stale_write and the write is rejected. Omit to skip the check. |
Any other field is refused with 422 invalid_request: sending user_id, agent_id, run_id or metadata in a PATCH is an error, not a no-op.
Example
from korely_memory import Korely
korely = Korely(api_key="kor_live_...", region="eu")
# Basic update, new content replaces the old one; facts are re-extracted.result = korely.update( "mem_8f2c1a", content="Northwind Hosting costs 55 euro per month after the storage add-on.",)print(result.status) # processingprint(result.facts) # [], the re-extracted facts land a few seconds later# Return values are dataclasses, use attribute access (result.status,# result.facts), never result["facts"].
# Later: read the current facts backfacts = korely.get_facts(entity="Northwind Hosting", user_id="customer-4812")print(facts[0].object) # 55 euro per month (newest first)
# With optimistic concurrency: pass the updated_at you last saw.# A 409 means someone else wrote to this memory since you read it.result = korely.update( "mem_8f2c1a", content="Northwind Hosting costs 55 euro per month after the storage add-on.", expected_updated_at="2026-06-07T09:14:00.412345+00:00",)Response
{ "id": "mem_8f2c1a", "content": "Northwind Hosting costs 55 euro per month after the storage add-on.", "agent_id": "infra-bot", "user_id": "customer-4812", "run_id": null, "metadata": { "source": "slack" }, "status": "processing", "created_at": "2026-06-07T09:14:00.412345+00:00", "updated_at": "2026-06-09T16:02:00.118201+00:00", "facts": []}
Once extraction has run, GET /v1/memories/mem_8f2c1a returns the
memory with status ready and the facts true now.
Facts the edit closed are not in it: each has invalid_at set and
invalidated_by naming the new fact (null when the new
text just stopped saying it). They still exist in the store: read them with
GET /v1/facts?include_invalidated=true (or as_of) and in
GET /v1/memories/mem_8f2c1a/history. Each closure also fires a
fact.invalidated webhook.
Response fields
| Field | Type | Meaning |
|---|---|---|
id | string | The memory's permanent identifier. Never changes across updates. |
content | string | The new content as stored after this write. |
agent_id | string or null | The agent namespace this memory belongs to. |
user_id | string or null | The end-user scope. Unchanged by an update. |
metadata | object | The metadata last written via add(). Not modified by update(). |
created_at | ISO 8601 string | Original creation timestamp. Never changes. |
updated_at | ISO 8601 string | Timestamp of this write. Use this as the next expected_updated_at if you plan to chain updates. |
status | string | processing while the new content is being extracted, then ready (or error). |
facts | array | Empty while status is processing, which is what the hosted service answers. Each item, once present, includes id, subject, predicate, object, predicate_family, valid_from, invalidated (the array of fact IDs this fact supersedes), tense, invalid_at and observation_count. |
facts[].invalidated | array of strings | IDs of facts from the previous version that this new fact supersedes. Empty when no contradiction was detected. |
Errors
| Status | Code string | Cause |
|---|---|---|
401 | invalid_key | The Authorization header is missing, malformed, or the key has been revoked. |
403 | forbidden | The key lacks the memories:write scope. |
404 | not_found | The id is malformed, no memory with that id exists in this key's project, or it was previously deleted. |
409 | stale_write | expected_updated_at was provided but does not match the server's current updated_at (or created_at, before the first edit). Another write happened since you last read this memory. Fetch the latest version and retry. |
422 | invalid_request | Request body failed validation, most commonly content is missing or empty, or the body has a field other than content and expected_updated_at. The body is the flat {"code":"invalid_request","message":"content: Field required"} envelope. |
429 | rate_limit_exceeded | Too many requests in the current minute, hour or day for your plan. The response carries Retry-After (integer seconds), X-RateLimit-Limit and X-RateLimit-Remaining: 0. |
429 | quota_exceeded | The monthly write quota is spent. An update counts one write per 6,000 characters of its new text, so past the quota it is refused and nothing changes. No Retry-After: the quota resets on the 1st of the month (UTC). |
503 | writes_paused | Only when extraction runs inside the call, not on the hosted service: the service's daily model budget is spent, nothing changed, retry after Retry-After (00:00 UTC). |
Notes
- Not idempotent. Each call to
PATCH /v1/memories/{id}re-runs extraction and updatesupdated_at. Sending the same content twice re-validates facts each time; the history keeps oneupdatedevent, with the time of the last edit. Useexpected_updated_atto guard against unintended repeated writes in retry loops. - Scoping is inherited, not settable. The
user_id,agent_id, andrun_idset at creation cannot be changed by an update. If you need to move a memory to a different scope, delete it and add a new one. - Metadata is not updated here. The
metadatafield is write-once fromadd(). If you need to attach new metadata, delete and re-add the memory. - Write quota. An update counts against your monthly write quota like
add()and each item of a batch import, one write per 6,000 characters of its text, because it runs extraction again; a direct fact write is one write. Like every/v1call, it also counts toward the per-minute, per-hour and per-day rate limits of your plan. - Fact history is preserved. Superseded facts are never hard-deleted. They remain visible in
GET /v1/memories/{id}/historyand in facts queries wheninclude_invalidated=trueis set. This lets you audit how knowledge evolved over time. - Contradiction detection is automatic. You do not need to locate and delete old facts manually. Pass the corrected content: the two-stage contradiction pipeline identifies and invalidates any conflicting facts within the same user scope, and the facts the new text no longer states are closed.
Related
- Add a memory, write new content and run the full extraction pipeline from scratch.
- Delete a memory, soft-delete one memory and close the facts only it stated.
- Search memories, retrieve the memories most relevant to a query before deciding whether to update.
- API reference, full endpoint contract including
GET /v1/memories/{id}/historyfor auditing past versions.