Skills and memories
Teach the workspace agent a skill and give it a memory about one of your records, using nothing but the records API every object already has.
Every workspace ships with two native objects, skill and memory. The agent reads skills to know how to do a task and memories to know what it learned about your data. Both are ordinary records: the same REST routes, filters, search, views, permissions and MCP tools your own objects get apply to them. This cookbook creates one of each from the SDK and shows the agent using them.
What you will build
A skill named invoice-followup that the agent follows the next time someone asks it to chase a payment, and a memory attached to one of your companies records that the agent finds when that company comes up. You verify both from the SDK, the CLI, the search endpoint and the app.
What a skill is
A skill is three attributes and, optionally, attached files:
| Attribute | Type | Limit | What the agent does with it |
|---|---|---|---|
name | text, required | 100 characters | The handle it calls get_skill with. |
description | text, required | 160 characters | One line in its prompt: what the skill does and when to use it. It reads every description, so this line decides whether the skill is picked. |
content | richtext | — | The instructions it reads once it has picked the skill. Written for the agent, not for people. |
New skills start from a template with the sections ## Overview, ## When to use, ## Instructions, ## Examples and ## What to avoid. Files attached to a skill travel with it: a reference PDF or a CSV template is copied next to the agent when it loads the skill.
What a memory is
A memory is a note the agent keeps across conversations, with a lifecycle:
| Attribute | Type | Values |
|---|---|---|
name, description, content | text, text, richtext | Same limits as a skill. |
kind | select | fact, preference, feedback, reference |
subjects | relation to any object, many | The records this memory is about. |
source | select | explicit is what save_memory writes. extracted, corroborated and curated are written by the workspace's own extraction and nightly consolidation. Nothing sets it for you over REST or the SDK: pass source: "explicit" yourself, as the samples do, or it stays empty. |
validFrom, invalidAt, supersededBy | date with time, date with time, relation to memory | A memory is never deleted by the agent: it is invalidated or superseded, and the history stays. |
reviewState, reviewReason | select, text | needs_review or skill_candidate. A memory in review is kept from the agent until cleared. |
A few more attributes (links, provenance, the curation timestamps) are maintained by the workspace; you can read them and leave them alone.
There is no owner attribute. A memory is workspace or private like any record, and a private one is visible only to the actor that created it.
Prerequisites
- A workspace, an API key whose role reads and writes
skill,memoryand your own objects, and@stndrds/clientinstalled. - One record of your own to attach the memory to. The steps use a
companiesrecord named Acme; any object works.
Look at the native objects
They are already there; nothing to sync. The sealed flag means their attributes are owned by Standards.
standards schema inspect skill
standards schema inspect memoryThrough MCP, get_schema with objectNames: ["skill", "memory"] returns the same definition in one call.
Do not declare an object named skill, memory, drives, artifact or meeting: object() throws ReservedObjectNameError at declaration, and a schema source that carries one is refused. Do not add attributes to them either; standards diff flags every attribute a sealed object did not declare as UNEXPECTED (sealed).
Create a skill
The client has no typed builder for native objects, so use the object's name. The description is the line the agent reads to decide; make it say what and when.
import { getStandards } from "./client";
const skills = getStandards().from("skill");
await skills.create({
name: "invoice-followup",
description: "Drafts a polite reminder for an overdue invoice. Use when asked to chase a payment.",
content: [
"## Overview",
"Write a short, courteous reminder for an invoice that is past its due date.",
"",
"## When to use",
"- When asked to chase, follow up on or remind about a payment.",
"",
"## Instructions",
"1. Read the invoice record: number, amount, due date, contact.",
"2. Write three sentences: the invoice, the amount and date, one clear ask.",
"3. Sign with the sender's first name only.",
"",
"## What to avoid",
"- Never threaten fees or legal action.",
].join("\n"),
});Names are unique on purpose: the four built-in skills (document-generation, agent-browser, ui, skill-creator) keep their names, and a record that reuses one is ignored.
Watch the agent use it
Open a conversation in the app and ask: "Chase the payment on invoice 2026-041." The agent's prompt lists up to 40 skills by description, the four built-ins first; it calls get_skill with the name, then follows the content. The call shows in the run, with the skill's name. No restart, no registration: the skill is read fresh on the next turn.
Give the agent a memory about a record
Attach the memory to the Acme record through subjects, so it surfaces whenever Acme is mentioned.
const companies = getStandards().from("companies");
const memories = getStandards().from("memory");
const acme = await companies.eq("name", "Acme").single();
if (!acme) throw new Error("create the Acme company first");
await memories.create({
name: "acme-prefers-french",
description: "Acme wants every message in French, addressed to Claire Morel.",
kind: "preference",
source: "explicit",
subjects: [acme.id],
});A record created this way is visible to the whole workspace. For a memory only you can see, create it over REST with the visibility option:
curl -X POST https://api.standards.new/v1/records/memory \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": { "name": "my-note-on-acme", "description": "Call after 14:00.", "kind": "fact", "source": "explicit" },
"options": { "visibility": "private" }
}'Find it back, four ways
The memory is a record, so every read path works. From the SDK, filter on the relation:
const about = await memories.in("subjects", [acme.id]).fetch();
about.records.map((memory) => memory.name); // ["acme-prefers-french"]From the CLI, full-text search on the object:
standards records search memory --query french --format tableFrom the workspace-wide search, restricted to the two native objects:
curl "https://api.standards.new/v1/search?q=french&objects=memory,skill" \
-H "Authorization: Bearer $STANDARDS_API_KEY"From the agent: @-mention the Acme record in a message (up to five records per turn count). The prompt gains a ## Memories about the records mentioned section listing each attached memory's name: description, and the agent opens one with get_memory before using it. Every live memory is also listed in the ## Memories index, up to 40, ranked by relevance to your message when there are more, attached ones first; past that the agent calls search_memories.
See both in the app
The sidebar lists Skills and Memories with the other intelligence objects. Skills open on their content with a Files tab, where you attach the reference PDF the skill mentions. Memories have four tabs: Memories, My memories, To review and History. Everything you create here is the same record you created from the SDK.
What the agent adds on its own
When it saves a memory itself, the agent calls save_memory, which first searches for close matches. If it finds any it saves nothing and returns the candidates; the agent then re-calls with updateMemoryId to update one, or force: true to create anyway. After a conversation, the workspace extracts new memories as private records, some flagged for review, with source: "extracted"; a nightly pass promotes the ones several people agree on and writes source: "corroborated" or curated. You never have to run any of that. As long as your own writes set source, the attribute tells what was saved by hand from what the workspace learned.
What you cannot do
- Change or delete the native objects' attributes, or name your own object after one of them.
- Exceed 100 characters on
nameor 160 ondescription; the API answersSCHEMA_VALIDATION_FAILED. - Call
save_memory,search_memories,get_memoryorget_skillover MCP: they are agent-only tools and never appear intools/list. Over MCP, read and write the two objects withsearch_records,get_record,create_recordandupdate_recordlike any object. - Set a memory's owner: ownership comes from the key that created it.
Next steps
- Records: every filter used above, including
inon a relation. - Objects and records: visibility,
workspaceversusprivate. - Tools: what an MCP client can do with
skillandmemory. - Agent workflow: the same objects from Claude Code.