Context & facts
Write a fact
Write a typed (subject, predicate, object) triple directly. Korely skips LLM extraction but still runs the contradiction check when the install has a model key, so a conflicting older fact is superseded, not duplicated.
/v1/facts
SDK: korely.add_fact_triple(subject, predicate, object, ...). Use
this when you already have a structured triple and want to skip extraction
(model_used is recorded as direct). With a model
key configured, the two-stage contradiction check still runs, which is why
the response carries
invalidated, the ids of any earlier facts this write
superseded.
Authentication
HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:write scope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | Required | Subject side of the triple. 1-200 characters; stripped before write. |
predicate | string | Required | Raw predicate; canonicalized server-side. 1-200 characters; stripped before write. |
object | string | Required | Object side of the triple. 1-500 characters; stripped before write. |
user_id | string | Optional | The end user this fact is about. 1 to 255 characters, no /, no control characters, not . or ..; omit it for the shared namespace. |
agent_id | string | Optional | Your application's namespace. Same rule as user_id. A new agent_id counts against your plan's agent cap on the Cloud. |
run_id | string | Optional | One session or run. Max 255 chars. |
subject_type | string | Optional | Subject type label, e.g. self or person. Default unknown. Max 20 chars. |
object_is_literal | boolean | Optional | Whether the object is a literal value rather than an entity. Default false. |
confidence | number | Optional | Confidence score between 0.0 and 1.0. Default 0.9. |
valid_from | string | Optional | ISO 8601 date or datetime the fact becomes valid. Sets the bi-temporal valid_from, use it when backfilling so the timeline stays correct. When omitted, the fact is valid from the moment of the write. |
tense | string | Optional | current, past or planned. Default current. A past fact closes the open fact it restates; with no open twin it is stored as history, already closed. |
Any other field is refused with 422 invalid_request: field names are snake_case.
Example request
curl -X POST https://api.korely.ai/v1/facts \ -H "Authorization: Bearer kor_live_..." \ -H "Content-Type: application/json" \ -d '{ "subject": "customer-giulia-4812", "predicate": "prefers", "object": "dark mode", "user_id": "customer-giulia-4812", "agent_id": "support-bot", "subject_type": "person", "object_is_literal": true, "confidence": 0.95 }'Response
201 Created. The newly written fact, plus the ids of any
earlier facts it superseded.
{ "id": "fct_a1", "subject": "customer-giulia-4812", "predicate": "prefers", "object": "dark mode", "predicate_family": "preferences", "valid_from": "2026-06-17T00:00:00+00:00", "invalidated": ["fct_a0"], "tense": "current", "invalid_at": null, "observation_count": 1, "user_id": "customer-giulia-4812", "agent_id": "support-bot"}| Field | Type | Description |
|---|---|---|
id | string | Public id of the newly written fact, e.g. fct_a1 (fct_ prefix). |
subject | string | Stored subject label. |
predicate | string | The predicate as stored: your phrase in snake_case form (prefers, works_at). A direct write keeps the name you chose, or the relation that name was already learned as in this vocabulary; the model never merges it into another relation. |
object | string | Stored object label. |
predicate_family | string · null | Predicate family derived from the canonical predicate. |
valid_from | string · null | ISO 8601 timestamp the fact is valid from, or null. |
invalidated | array<string> | Public ids of pre-existing facts this write superseded via the contradiction check. Empty list if none. |
invalid_at | string · null | ISO 8601 timestamp the fact stopped being true. Set when you write a past fact; null otherwise. |
tense | string | The tense the fact was stored with: current, past or planned. |
observation_count | integer | How many times this fact has been stated. Writing a triple that is already open confirms it and raises the count instead of adding a duplicate. |
user_id | string or null | The end user the fact belongs to, as in GET /v1/facts. |
agent_id | string or null | The agent namespace it was written under, as in GET /v1/facts. |
Contradictions resolve on write. When the install has a
model key, as the Cloud does, a current fact goes through
the two-stage contradiction check even on a direct write: open facts of
the same subject that could conflict are gathered, and a model judges
whether the new fact ends them, reading whether the relation holds one
value at a time. A fact it ends is closed, not deleted, and its id
appears in invalidated. Superseded facts stay queryable via
as_of. A Self-hosted install with no model key runs no judge
and closes nothing this way; a past fact still closes the
open fact it restates.
Errors
| Status | Code | Cause |
|---|---|---|
401 | invalid_key | Missing or invalid kor_live_ key in the Authorization header. |
403 | forbidden | The API key lacks the memories:write scope. |
413 | payload_too_large | The body is larger than 2,097,152 bytes. Refused before it is read; nothing is written or counted. On the Cloud a body over 1 MB gets a bare 413 first, without this code. |
429 | agent_cap_exceeded | Writing with a new agent_id would exceed your plan's named agents (the body adds limit and used). Reuse an existing agent_id, delete one with DELETE /v1/agents/{agent_id}, or upgrade. Checked before the write quota, so a capped write doesn't burn a quota count. |
429 | rate_limit_exceeded | Per-plan rate limit exceeded. Retry after the Retry-After header. |
429 | quota_exceeded | The month's writes are used up, 10% grace included; nothing is written. The body adds limit, used and resets_at (00:00 UTC on the 1st) to code and message. Top up the month on a paid plan, or upgrade. |
422 | invalid_request | Body validation failed: subject/predicate/object missing or over their length limits, confidence outside [0, 1], a non-ISO valid_from, a tense other than the three values, a user_id or agent_id breaking the id rule, or an unknown field. |
503 | model_unavailable | The model that resolves the relation against your learned vocabulary, or the contradiction check, gave no usable answer. Nothing was written; retry the request. |
Notes
- Skips extraction, not the check. A direct write bypasses LLM extraction (
model_usedisdirect) but still runs the two-stage contradiction check when the install has a model key, that's why the response carriesinvalidated. - Idempotent by triple. If an active fact already exists with the same subject, canonical predicate, and object for the same end user and
agent_id, Korely reconfirms it instead of writing a duplicate: the answer is still201, with the sameid,invalidated: []andobservation_countone higher. - Values are stripped.
subject,predicate, andobjectare trimmed before write. - Agent cap before quota. When you pass a new
agent_id, the agent-cap check runs before the write-quota check, so a cap failure never consumes a quota slot. - Telemetry. The write commits, emits invalidation events for any superseded facts, and is logged as a
facts:createrequest.
Related
- Get facts, read the typed facts back.
- Get context, the assembled recall block, facts included.
- SDK reference, every method, typed.