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

# Manage Learnings

> Read and manage learning records through the AgentOS /learnings endpoints.

AgentOS exposes `/learnings` REST endpoints for CRUD over the `agno_learnings` table, the table that backs the `user_profile`, `user_memory`, `session_context`, `entity_memory`, and `decision_log` stores (`learned_knowledge` lives in the knowledge base instead). Enable learning on an agent and serve it with AgentOS, and the endpoints are available automatically.

## Prerequisites

* A supported database: PostgreSQL, SQLite, or MongoDB. Other databases return `501`.
* An agent with learning enabled (see the [Learning quickstart](/learning/quickstart)).

## Example

```python learnings_with_agentos.py theme={null}
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.learn import LearningMachine
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS

db = SqliteDb(id="learnings-os-demo", db_file="tmp/learnings_os_demo.db")

learning = LearningMachine(
    db=db,
    model=OpenAIResponses(id="gpt-5.4"),
    user_profile=True,
    user_memory=True,
    namespace="global",
)

assistant = Agent(
    name="Assistant",
    model=OpenAIResponses(id="gpt-5.4"),
    instructions=["You are a helpful assistant. Use what you know about the user."],
    db=db,
    learning=learning,
)

agent_os = AgentOS(agents=[assistant])
app = agent_os.get_app()

if __name__ == "__main__":
    agent_os.serve(app="learnings_with_agentos:app", reload=True)
```

Browse the interactive OpenAPI docs at `http://localhost:7777/docs`.

## Endpoints

| Method   | Path                         | Description                                                  |
| -------- | ---------------------------- | ------------------------------------------------------------ |
| `GET`    | `/learnings`                 | Paginated list with filters and sorting                      |
| `POST`   | `/learnings`                 | Create a record                                              |
| `GET`    | `/learnings/users`           | List users that own learnings, with last-activity timestamps |
| `DELETE` | `/learnings/users/{user_id}` | Delete all of a user's learnings (or one type)               |
| `GET`    | `/learnings/{learning_id}`   | Fetch a single record                                        |
| `PATCH`  | `/learnings/{learning_id}`   | Update `content` and/or `metadata`                           |
| `DELETE` | `/learnings/{learning_id}`   | Delete a record                                              |

Every endpoint accepts `db_id` and `table` query parameters to target a specific database or table. `table` requires `db_id`.

## Listing and filtering

`GET /learnings` returns a paginated envelope: `data` holds the records and `meta` holds the pagination info (`page`, `limit`, `total_pages`, `total_count`).

```bash theme={null}
curl "http://localhost:7777/learnings?user_id=demo-user&limit=10&page=1"
```

| Parameter                                      | Description                                                        |
| ---------------------------------------------- | ------------------------------------------------------------------ |
| `learning_type`                                | Filter by store (`user_profile`, `user_memory`, etc.)              |
| `user_id`, `agent_id`, `team_id`, `session_id` | Filter by owner                                                    |
| `namespace`, `entity_id`, `entity_type`        | Filter by scope or entity                                          |
| `limit`                                        | Page size (1–1000, default 100)                                    |
| `page`                                         | 1-indexed page number                                              |
| `sort_by`                                      | `created_at` or `updated_at` (default). Unknown fields are ignored |
| `sort_order`                                   | `asc` or `desc` (default)                                          |

For a per-user view, list users with `GET /learnings/users`, then drill into one with `GET /learnings?user_id=...`.

## Creating records

```bash theme={null}
curl -X POST http://localhost:7777/learnings \
  -H "Content-Type: application/json" \
  -d '{
    "learning_type": "user_profile",
    "namespace": "global",
    "user_id": "demo-user",
    "content": {"user_id": "demo-user", "name": "Yash", "preferred_name": "Yash"},
    "metadata": {"source": "rest-api-demo"}
  }'
```

Identity-keyed learning types use deterministic IDs derived from their identity fields. `POST` computes the **same** ID, so a record created through the API reconciles with what the agent reads and writes without creating orphaned or duplicate rows.

| `learning_type`   | Derived ID                                     | Required identity fields                                      |
| ----------------- | ---------------------------------------------- | ------------------------------------------------------------- |
| `user_profile`    | `user_profile_{user_id}`                       | `user_id`                                                     |
| `user_memory`     | `memories_{user_id}`                           | `user_id`                                                     |
| `session_context` | `session_context_{session_id}`                 | `session_id`                                                  |
| `entity_memory`   | `entity_{namespace}_{entity_type}_{entity_id}` | `entity_type`, `entity_id` (`namespace` defaults to `global`) |

* Provide the required identity field(s), or the request returns `422`.
* Include the same identity fields inside `content` so the agent's store can deserialize the record.
* An existing record for that identity returns `409`. Use `PATCH` to update it.
* Other types (for example, `decision_log`) get a generated ID, so a user can have many.

## Updating records

`PATCH` replaces `content` and/or `metadata`. Identity fields are immutable.

```bash theme={null}
curl -X PATCH http://localhost:7777/learnings/user_profile_demo-user \
  -H "Content-Type: application/json" \
  -d '{"content": {"user_id": "demo-user", "name": "Yash", "preferred_name": "Yash P."}}'
```

## Deleting records

```bash theme={null}
# Delete a single record
curl -X DELETE http://localhost:7777/learnings/user_profile_demo-user

# Delete all of a user's learnings (add ?learning_type= to restrict to one store)
curl -X DELETE http://localhost:7777/learnings/users/demo-user
```

Both return `204`. The user-level delete never touches records with no owner.

## Authorization and isolation

Scoping follows the framework's opt-in [user isolation](/agent-os/security/authorization/user-isolation) contract (`AuthorizationConfig(user_isolation=True)`). Admins and requests with isolation disabled are unscoped. Anonymous requests are unscoped only on an open instance. JWT-enabled instances reject missing tokens with `401`, static security-key callers are unscoped, and service-account PATs always self-scope unless they carry the admin scope. For a scoped non-admin caller:

| Operation                    | Behavior                                                                                                                         |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| List / list users            | Bound to the caller. List also includes records with no owner (`user_id IS NULL`). A different `user_id` returns `403`           |
| Create                       | Body `user_id` must be omitted/null or match the caller, otherwise `403`                                                         |
| Delete user                  | Only the caller's own learnings; a different `user_id` returns `403`                                                             |
| Get single record            | A cross-user record returns `404` (no existence leak)                                                                            |
| Patch / delete single record | Cross-user returns `404`. Shared records (`user_id IS NULL`) are readable but admin-only to mutate, so a regular user gets `403` |

When [RBAC](/agent-os/security/authorization/scopes) is enabled, the routes require the `learnings:read`, `learnings:write`, or `learnings:delete` scopes.

## Developer Resources

* [Learnings API reference](/reference-api/schema/learnings/list-learnings)
* [Learning overview](/learning/overview)
* [Learning stores](/learning/stores/intro)
* [User isolation](/agent-os/security/authorization/user-isolation)
