> ## Documentation Index
> Fetch the complete documentation index at: https://docs.riloworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LearnedOnce Memory API

> Give any agent a memory that learns. remember / recall / forget over HTTPS, metered per operation, with a shared pool of how-to knowledge every tenant's agent can draw on.

LearnedOnce is a memory service for AI agents — the same layer that powers Rilo's own agent,
available to anyone building or using agents. Your agent sends what it learned; later it asks what it
knows. Facts stay private to you. Reusable *procedures* — how to file a Jira ticket, how a
vendor's API paginates — pool into a shared namespace so every agent on the platform gets
better as any one of them learns.

## Why a memory layer

Every agent call today re-sends context it already paid for last time: the tool docs it read,
the failed attempt it recovered from, the user's preference it was told yesterday. A memory
that is *retrieved* instead of *re-derived* cuts the tokens you send and receive, shortens the
path to a correct action, and removes a whole class of repeat mistakes. In Rilo's own agent,
warm-memory runs plan from proven prior attempts instead of from scratch; we publish the
measured token/latency/error deltas on this page as the numbers land (bead `agent-stack-p7eu`).

## Authentication

Create a key in the customer portal (**Memory → API keys**), then send it as `X-Api-Key`.

```bash theme={null}
export RILO_MEMORY_KEY=rilo_mem_...
```

Keys are org-scoped. Every memory you write is stored in a namespace that belongs to your
organization only; no filter is involved in that isolation.

## Base URL

```
https://api.learnedonce.com/v1/memory
```

## Remember

```bash theme={null}
curl -X POST https://api.learnedonce.com/v1/memory/remember \
  -H "X-Api-Key: $RILO_MEMORY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "To create a Jira issue: POST /rest/api/3/issue with {fields:{project:{key},summary,issuetype:{name}}}; 400 means a required custom field is missing.",
    "kind": "procedure",
    "collection": "jira",
    "tags": ["jira", "api"],
    "attributes": {"learned_from": "task_8842"}
  }'
```

| Field        | Type                                | Notes                                                                                                                                                                                  |
| ------------ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`       | string, ≤30,000 chars               | The memory. Embedded for semantic recall.                                                                                                                                              |
| `kind`       | `"fact"` (default) or `"procedure"` | `fact` never leaves your namespace. `procedure` is reusable how-to knowledge; if your key contributes, it is mirrored into the shared pool with your identity and attributes stripped. |
| `collection` | string, ≤64 chars, no whitespace    | Bucket to recall within (a user id, a project, an agent).                                                                                                                              |
| `tags`       | ≤32 strings                         | Filterable labels.                                                                                                                                                                     |
| `attributes` | ≤32 key/value strings               | Returned verbatim on recall. Never pooled.                                                                                                                                             |
| `id`         | string, optional                    | Supply to upsert (overwrite) an existing memory.                                                                                                                                       |

Response `201`:

```json theme={null}
{"id": "9c1e…", "text": "…", "kind": "procedure", "collection": "jira", "tags": ["api","jira"],
 "attributes": {"learned_from": "task_8842"}, "created_at": "2026-08-25T21:03:11+00:00",
 "score": null, "pooled": true, "pool_status": "pooled"}
```

`pool_status` is one of `pooled`, `not_pooled` (facts, or a non-contributing key),
`pool_rejected`, or `pool_unavailable` — your own write is durable in every case.

## Recall

```bash theme={null}
curl -X POST https://api.learnedonce.com/v1/memory/recall \
  -H "X-Api-Key: $RILO_MEMORY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "how do I open a Jira ticket from the API", "top_k": 5, "tags": ["jira"]}'
```

| Field                        | Type                 | Notes                                                     |
| ---------------------------- | -------------------- | --------------------------------------------------------- |
| `query`                      | string               | Natural-language question or the task at hand.            |
| `top_k`                      | 1–50, default 5      | Results after merging your memories with the shared pool. |
| `collection`, `tags`, `kind` | optional filters     | `kind: "fact"` skips the shared pool entirely.            |
| `min_score`                  | 0–1                  | Similarity floor.                                         |
| `include_shared`             | bool, default `true` | Set `false` to search only your namespace.                |

Response `200`:

```json theme={null}
{"memories": [
   {"id": "pooled-…", "text": "To create a Jira issue: POST /rest/api/3/issue …",
    "kind": "procedure", "shared": true, "score": 0.91, "tags": ["api","jira"], "attributes": {}},
   {"id": "3f…", "text": "Acme's Jira project key is OPS", "kind": "fact", "shared": false,
    "score": 0.78, "attributes": {"learned_from": "task_8842"}}
 ],
 "shared_pool": "ok",
 "balance_minutes": 118.4}
```

`shared_pool` is `ok`, `not_searched` (you set `include_shared: false` or `kind: "fact"`),
or `pool_unavailable` (your own memories were returned; the pool was unreachable).

Results carry `shared: true` when they came from the pool. Pooled memories never contain
another tenant's attributes, collection names, or identity.

## Get / Forget

```bash theme={null}
curl https://api.learnedonce.com/v1/memory/{id} -H "X-Api-Key: $RILO_MEMORY_KEY"
curl -X DELETE https://api.learnedonce.com/v1/memory/{id} -H "X-Api-Key: $RILO_MEMORY_KEY"
```

Forget is free. It removes the memory from your namespace and retracts any pooled mirror
of it. Re-writing a pooled procedure as a `fact` (same `id`) also retracts the mirror.

## Usage

```bash theme={null}
curl https://api.learnedonce.com/v1/memory/usage -H "X-Api-Key: $RILO_MEMORY_KEY"
# {"balance_minutes": 118.4, "seconds_per_operation": 0.1, "operations_remaining": 71040}
```

## Pricing and metering

Every `remember` and `recall` debits **0.1 agent-seconds** from your prepaid Rilo balance
before the operation runs — about \*\*$0.14 per 1,000 operations** at the standard $5 per
agent-hour rate. Gets and deletes are free. When the balance cannot cover an operation you
receive `402` with a `top_up_url`; nothing is written. If the store fails after the debit
you receive `503` and the debit is refunded — retry. Your starter credit covers your first
thousands of operations with no card.

## Shared learning and contribution

* **Facts are never shared.** Anything you write with `kind: "fact"` (the default) stays in
  your namespace, full stop.
* **Procedures pool by default.** A contributing key mirrors `procedure` memories into the
  shared pool with your organization, collection, and attributes stripped. Every tenant's
  `recall` can then surface them.
* **Contribution is required on free usage** and can be switched off per key once your
  organization has made a payment (enterprise agreements can opt out entirely).
* All writes pass Rilo's memory write guard (prompt-injection and contradiction screening)
  before they are stored.

## Errors

| Status | Meaning                                                                  |
| ------ | ------------------------------------------------------------------------ |
| `401`  | Missing, unknown, or revoked `X-Api-Key`.                                |
| `402`  | Insufficient prepaid balance — body includes `top_up_url`.               |
| `404`  | Memory not found, or the Memory API is not enabled for this environment. |
| `422`  | Validation failure, or the write guard rejected the text (refunded).     |
| `503`  | Memory store unavailable — debit refunded, safe to retry.                |
