Context & facts
Correct a fact
A correction writes a new fact and closes the old one. It is not an edit: the old row keeps its dates and gains a pointer to the new one, so as_of before the correction still returns what you believed then.
/v1/facts/{fact_id}
SDK: korely.correct_fact(fact_id, object=...). A store that
edits a row in place cannot answer "what did we think in June", and
answering that is the point of keeping history.
Authentication
HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:write scope.
Request body
At least one of the three. Anything you leave out is carried over from the fact you are correcting.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | Optional | Corrected subject. 1-200 characters. |
predicate | string | Optional | Corrected predicate; canonicalized server-side. 1-200 characters. |
object | string | Optional | Corrected object. 1-500 characters. |
Any other field is refused with 422 invalid_request.
Example request
curl -X PATCH https://api.korely.ai/v1/facts/fct_a1 \ -H "Authorization: Bearer kor_live_..." \ -H "Content-Type: application/json" \ -d '{"object": "Basic plan"}'Response
200 OK. The new fact, with the id of the one it superseded.
{ "id": "fct_a2", "subject": "customer-giulia-4812", "predicate": "subscribes_to", "object": "Basic plan", "predicate_family": "ownership", "valid_from": "2026-06-20T09:14:02+00:00", "invalidated": ["fct_a1"], "tense": "current", "invalid_at": null, "observation_count": 1, "user_id": "customer-giulia-4812", "agent_id": "support-bot"}
The write shape of Write a fact:
invalidated lists the corrected fact first, then any other fact the
contradiction check closed.
Only a current fact is corrected
Correct the fact that holds now. A fact that is history (superseded by a
newer one, forgotten, or ended) answers 409 fact_not_current.
current_fact_id is the fact to correct instead, so a client
retries on it without reading the message:
{ "detail": {"code": "fact_not_current", "message": "This fact is no longer current, so it cannot be corrected. It was superseded by fct_a2: correct that one instead.", "current_fact_id": "fct_a2"}, "code": "fact_not_current", "message": "This fact is no longer current, so it cannot be corrected. It was superseded by fct_a2: correct that one instead.", "current_fact_id": "fct_a2"}
When no fact holds now, current_fact_id is null and
the message says to write the value that holds now with POST /v1/facts. To
change what was true in the past, write what happened with its dates
there; a correction never closes the value in force because of an old row.
Errors
| Status | Code | Cause |
|---|---|---|
401 | invalid_key | Missing or invalid kor_live_ key. |
403 | forbidden | The key lacks the memories:write scope. |
404 | not_found | The id is malformed, unknown, or the fact is in another project: No fact with that id. No write is counted. With the month's quota spent, a well-formed unknown id gets the 429 first. |
409 | fact_not_current | The fact is no longer current: superseded, forgotten or ended. current_fact_id is the current fact, null when there is none. Nothing is written and no write is counted (the reserved write is given back). With the month's quota spent, the 429 comes first. |
413 | payload_too_large | The 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. |
422 | invalid_request | The body is empty or every field is blank (Give at least one of subject, predicate or object.), a field is over its length limit, or a field is unknown. |
429 | quota_exceeded | Monthly writes used up, 10% grace included. Nothing changes. detail carries limit, used and resets_at. |
429 | rate_limit_exceeded | Per-minute, per-hour or per-day request limit. Carries Retry-After. |
503 | model_unavailable | The model that resolves the relation, or the contradiction check, gave no usable answer, or the service's daily model budget is spent (until 00:00 UTC). Nothing was written; retry. |
Notes
- An empty body is refused with
422. A body that restates the open fact as it is answers200with the sameidandinvalidated: [], and still uses one write. - It counts as one write. A correction uses one write of your monthly quota, like any
PATCH; past the quota it answers429 quota_exceededand nothing changes. A404or a409costs nothing; with the month's quota spent, a429comes first. - Scoped to your key's project. A fact in another project answers
404, the same answer as an id that never existed. - Both stay readable. The old fact comes back with
include_invalidated=trueand in anyas_ofread before the correction.