Skip to main content
AgentOS exposes /learnings REST endpoints for CRUD over the agno_learnings table, the table that backs every learning store (user_profile, user_memory, session_context, entity_memory, decision_log). 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).

Example

learnings_with_agentos.py
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

MethodPathDescription
GET/learningsPaginated list with filters and sorting
POST/learningsCreate a record
GET/learnings/usersList 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).
curl "http://localhost:7777/learnings?user_id=demo-user&limit=10&page=1"
ParameterDescription
learning_typeFilter by store (user_profile, user_memory, etc.)
user_id, agent_id, team_id, session_idFilter by owner
namespace, entity_id, entity_typeFilter by scope or entity
limitPage size (1–1000, default 100)
page1-indexed page number
sort_bycreated_at or updated_at (default). Unknown fields are ignored
sort_orderasc 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

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", "preferences": {"language": "Python"}},
    "metadata": {"source": "rest-api-demo"}
  }'
The learning stores key records by a deterministic ID derived from their identity fields, not a random UUID. POST computes the same ID for the identity-keyed types, so a record created through the API reconciles with what the agent reads and writes. No orphan, no duplicate.
learning_typeDerived IDRequired identity fields
user_profileuser_profile_{user_id}user_id
user_memorymemories_{user_id}user_id
session_contextsession_context_{session_id}session_id
entity_memoryentity_{namespace}_{entity_type}_{entity_id}namespace, entity_type, entity_id
  • 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.
curl -X PATCH http://localhost:7777/learnings/user_profile_demo-user \
  -H "Content-Type: application/json" \
  -d '{"content": {"name": "Yash", "preferences": {"language": "Python", "tone": "concise"}}}'

Deleting records

# 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, and never touch records with no owner.

Authorization and isolation

Scoping follows the framework’s opt-in user isolation contract (AuthorizationConfig(user_isolation=True)). Admins, requests with isolation disabled, and requests without a JWT are unscoped and have full access. For a scoped (non-admin) caller:
OperationBehavior
List / list usersBound to the caller. List also includes records with no owner (user_id IS NULL). A different user_id returns 403
CreateBody user_id must be omitted/null or match the caller, otherwise 403
Delete userOnly the caller’s own learnings; a different user_id returns 403
Get single recordA cross-user record returns 404 (no existence leak)
Patch / delete single recordCross-user returns 404. Shared records (user_id IS NULL) are readable but admin-only to mutate, so a regular user gets 403
When RBAC is enabled, the routes require the learnings:read, learnings:write, or learnings:delete scopes.

Developer Resources