Korely

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.

POST /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

FieldTypeRequiredDescription
subjectstringRequiredSubject side of the triple. 1-200 characters; stripped before write.
predicatestringRequiredRaw predicate; canonicalized server-side. 1-200 characters; stripped before write.
objectstringRequiredObject side of the triple. 1-500 characters; stripped before write.
user_idstringOptionalThe end user this fact is about. 1 to 255 characters, no /, no control characters, not . or ..; omit it for the shared namespace.
agent_idstringOptionalYour application's namespace. Same rule as user_id. A new agent_id counts against your plan's agent cap on the Cloud.
run_idstringOptionalOne session or run. Max 255 chars.
subject_typestringOptionalSubject type label, e.g. self or person. Default unknown. Max 20 chars.
object_is_literalbooleanOptionalWhether the object is a literal value rather than an entity. Default false.
confidencenumberOptionalConfidence score between 0.0 and 1.0. Default 0.9.
valid_fromstringOptionalISO 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.
tensestringOptionalcurrent, 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

Terminal window
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"
}
FieldTypeDescription
idstringPublic id of the newly written fact, e.g. fct_a1 (fct_ prefix).
subjectstringStored subject label.
predicatestringThe 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.
objectstringStored object label.
predicate_familystring · nullPredicate family derived from the canonical predicate.
valid_fromstring · nullISO 8601 timestamp the fact is valid from, or null.
invalidatedarray<string>Public ids of pre-existing facts this write superseded via the contradiction check. Empty list if none.
invalid_atstring · nullISO 8601 timestamp the fact stopped being true. Set when you write a past fact; null otherwise.
tensestringThe tense the fact was stored with: current, past or planned.
observation_countintegerHow 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_idstring or nullThe end user the fact belongs to, as in GET /v1/facts.
agent_idstring or nullThe 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

StatusCodeCause
401invalid_keyMissing or invalid kor_live_ key in the Authorization header.
403forbiddenThe API key lacks the memories:write scope.
413payload_too_largeThe 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.
429agent_cap_exceededWriting 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.
429rate_limit_exceededPer-plan rate limit exceeded. Retry after the Retry-After header.
429quota_exceededThe 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.
422invalid_requestBody 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.
503model_unavailableThe 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_used is direct) but still runs the two-stage contradiction check when the install has a model key, that's why the response carries invalidated.
  • 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 still 201, with the same id, invalidated: [] and observation_count one higher.
  • Values are stripped. subject, predicate, and object are 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:create request.

Related