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:
{ "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. Answered before the quota: no write is counted. |
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. Answered before the quota: nothing is written and no write is counted. |
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. The body adds 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. Nothing was written; retry. |
503 | writes_paused | The service's daily model budget is spent. Nothing was written; retry after Retry-After (00:00 UTC). |
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 a409comes before the quota and costs nothing. - 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.