Build with the AI
Memories
Save and manage the durable facts the AI remembers about you.
A memory is an explicitly-saved fact about you — “prefers TypeScript over
JavaScript”, “timezone is CET”, “deploys on Fridays”.
Unlike a conversation turn, a memory is user-global: it is not tied to any single chat,
guild, or persona, and it follows you across every AI surface on your account. The
/api/v1/memories endpoints let you read, create and delete these facts programmatically,
the same store the AI itself writes to when it decides something is worth remembering.
Memories are scoped to the owner of the API key, not to a conversation. A fact you
POST here is visible to a later GET or DELETE from any key the same
owner holds, and is injected into that owner’s AI context everywhere.
How memories work
Three properties are worth internalizing before you write against this API:
-
User-global, not per-conversation. Facts are stored with no channel, guild, or persona
binding. A fact saved from anywhere is recalled everywhere for that owner. There is deliberately no
“save this only for this thread” option — for conversation-scoped context, keep it in your
chat completions
messagesarray instead. -
Plain-text, capped and truncated. A memory is a single free-text string — there is no
key/value structure. Content is trimmed of surrounding whitespace and then hard-truncated to a maximum
length (500 characters by default, set by the
AI_MEMORY_FACT_MAX_CHARSserver config). Truncation is silent: over-length input is accepted and stored as its first N characters (re-trimmed), never rejected. Save one crisp fact per call, not a multi-paragraph summary. -
Governed by the account memory switch. If memory is turned off for the account, saving
a fact is refused with
409 memory_disabled. Reading and deleting are unaffected. See Saving with memory off.
To steer how the AI reasons rather than what it knows about you, attach your own persona to a request — personas and memories are independent and compose freely.
Scopes
Each memory operation requires the matching scope on your API key:
| Operation | Method | Scope |
|---|---|---|
| List memories | GET | memories:read |
| Save a memory | POST | memories:write |
| Delete a memory | DELETE | memories:write |
A request missing the required scope returns 403 insufficient_scope with the needed scope
echoed in error.required_scope. Grant scopes when you mint a key in the
Developer console; see Scopes and
Authentication.
The memory object
Every endpoint that returns a memory serializes it with exactly these fields:
| Field | Type | Description |
|---|---|---|
id | integer | Stable identifier for the fact. Use it to delete the memory. |
content | string | The stored fact, trimmed and truncated to the configured maximum length. |
created_at | string · null | ISO-8601 UTC instant the fact was saved (e.g. 2026-07-14T18:32:05Z). May be null if the timestamp is unavailable. |
Responses use the native Krallo envelope: successful bodies are wrapped as
{ "data": …, "meta": { "request_id": … } }. Errors are
{ "error": { "code", "message", … }, "meta": … }. See
Errors for the full contract.
List memories
/api/v1/memories
Returns your saved facts as a JSON array, newest first (by creation time, ties broken by
descending id). Requires memories:read.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | No | Maximum number of facts to return. Defaults to 50. Clamped to the range 1–200; a missing or non-numeric value falls back to the default. |
Request
cURLcurl "https://exe.krallo.com/api/v1/memories?limit=10" \
-H "Authorization: Bearer krallo_live_..."
Response
{
"data": [
{
"id": 8241,
"content": "Prefers TypeScript and pnpm; deploys are gated behind CI.",
"created_at": "2026-07-14T18:32:05Z"
},
{
"id": 8109,
"content": "Primary timezone is CET (Europe/Berlin).",
"created_at": "2026-07-11T09:14:47Z"
}
],
"meta": { "request_id": "5f3a91c0-b2d4-4e16-9a58-c07d2f4b8e31" }
}
An owner who has never saved a fact receives "data": [] with a 200 status.
Save a memory
/api/v1/memories
Saves a single user-global fact and returns it. Requires memories:write. Responds
201 Created on success.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
content |
string | Yes | The fact to remember. Must be non-blank. Leading/trailing whitespace is trimmed, then the text is truncated to AI_MEMORY_FACT_MAX_CHARS characters (default 500). A missing or blank value returns 400 invalid_request. |
Request
cURLcurl https://exe.krallo.com/api/v1/memories \
-H "Authorization: Bearer krallo_live_..." \
-H "Content-Type: application/json" \
-d '{ "content": "Prefers TypeScript and pnpm; deploys are gated behind CI." }'
Response
{
"data": {
"id": 8241,
"content": "Prefers TypeScript and pnpm; deploys are gated behind CI.",
"created_at": "2026-07-14T18:32:05Z"
},
"meta": { "request_id": "9c1e77aa-04f3-4b8d-a25c-6e19d0b74f82" }
}
In the rare case the row cannot be read back immediately after insertion, the response still confirms
the write with a minimal body — { "id": 8241, "saved": true } — instead of the full object.
The fact is saved either way; fetch it later with List memories.
Saving with memory off
If memory is disabled for the account, a save is refused with 409 memory_disabled
and nothing is stored. This is checked before the call is billed, and re-checked at write time to close the
race where the switch is toggled off mid-request:
{
"error": {
"code": "memory_disabled",
"message": "Memory is disabled for this account. Enable it before saving facts."
},
"meta": { "request_id": "2b40de19-f7a6-4c53-8d90-1e4b7a2c5f68" }
}
Re-enable memory in your account’s AI privacy settings, then retry the save.
Delete a memory
/api/v1/memories/{id}
Permanently deletes one saved fact by id. Requires memories:write. Deletion is owner-scoped:
you can only delete your own facts, and an id that does not exist (or belongs to another owner) returns
404 not_found rather than revealing anything about it.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The id of the memory to delete, from a list or save response. A non-numeric path segment returns 404 not_found. |
Request
cURLcurl -X DELETE https://exe.krallo.com/api/v1/memories/8241 \
-H "Authorization: Bearer krallo_live_..."
Response
{
"data": { "id": 8241, "deleted": true },
"meta": { "request_id": "e70b2c88-15d9-4a37-b642-90c1f5e8a3d4" }
}
Memories are immutable — there is no update. A PATCH or PUT
against /api/v1/memories/{id} returns 405 method_not_allowed, and there is no
endpoint to fetch a single memory. To change a fact, delete it and POST a new one.
Billing & rate limits
Every memory call — list, save, and delete — is a flat metered request. Calls first draw down your app’s
free daily allowance; once that is exhausted, and if wallet billing is enabled, each call debits a small,
fixed number of credit units from the owner’s balance. If the wallet cannot cover a billable call, the
request returns 402 insufficient_credits with required_units and
balance in the error body, and nothing is changed. Refused or errored calls are refunded and do
not burn quota. See Billing for the full model.
Requests are rate-limited per key by your app’s tier. Every authenticated response carries
X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset
headers (pre-auth 401/403 responses do not); exceeding the limit returns
429 with a Retry-After header. See
Rate limits.
Errors
Memory endpoints can return the following errors, in addition to the standard bearer-auth failures:
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | content is missing or blank on a save. |
| 401 | unauthorized | No bearer token was supplied. |
| 401 | invalid_token · token_revoked · token_expired | The key is unknown, revoked, or past its expiry. |
| 402 | insufficient_credits | Over the free quota and the wallet cannot cover the billable call. |
| 403 | insufficient_scope | The key lacks memories:read (read) or memories:write (save/delete). |
| 403 | app_disabled · owner_suspended | The owning app is disabled, or the account is suspended. |
| 404 | not_found | Delete of an unknown or non-owned id, a non-numeric id, or the developer platform is disabled. |
| 405 | method_not_allowed | An unsupported method, e.g. PATCH/PUT on a memory, or GET on a single-memory path. |
| 409 | memory_disabled | Memory is turned off for the account; saving is refused. |
| 429 | rate_limited | The per-key rate limit was exceeded. |
| 500 | internal_error | An unexpected server-side failure; the call is refunded. |
Apart from the 429 — written by the shared rate limiter as a bare
{ "error": "rate_limited", "hint", "retryAfterSeconds" } body — all errors share the
native envelope shape. For status semantics and retry guidance, see
Errors. Related reading:
Personas,
Chat completions, and
Authentication.