roasOS
Brand

Memory

The pinned facts and the marketing brief that the roasOS agent reads before it writes.

Memory is what you teach the agent. It has two parts, and both parts sit behind /v1/memory.

PartPathWhat it is
Pins/v1/memory/pinsshort facts, one line each, in six categories
Brief/v1/memory/briefone structured marketing brief

The chat agent reads the same rows. A pin that you write over the API reaches the agent in the app, and a pin that the agent writes reaches this API.

Every door here costs 0 credits.

A 0-credit door never returns a quote envelope. It needs no Idempotency-Key, it accepts no quote_only, and it accepts no quote_id.

OperationPathScopeReference
List the pinsGET /v1/memory/pinsapi:readlistMemoryPins
Pin a factPOST /v1/memory/pinsapi:writecreateMemoryPin
Unpin a factDELETE /v1/memory/pins/{id}api:writedeleteMemoryPin
Read the briefGET /v1/memory/briefapi:readgetMemoryBrief
Replace the briefPUT /v1/memory/briefapi:writeupdateMemoryBrief

Pins

List the pins

curl "https://api.roasos.com/v1/memory/pins?limit=20&category=hook" \
  -H "Authorization: Bearer roasos_sk_..."
{
  "data": [
    {
      "id": "b2a1c0de-...",
      "category": "hook",
      "content": "Open on the price objection. It beats a product shot for this audience.",
      "metadata": {},
      "created_at": "2026-08-21T10:20:00.000Z"
    }
  ],
  "meta": { "has_more": false }
}

The list is newest first. Each timestamp is UTC and ends with Z. limit and cursor work as they do on every list. Refer to Pagination.

Add category to read one category only. A category value outside the six is 400 validation_error.

The marketing brief is not a pin, so this list never returns it.

Pin a fact

curl -X POST https://api.roasos.com/v1/memory/pins \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "category": "banned",
    "content": "Never say clinically proven. Legal refused the claim."
  }'

The answer is 201 with the new pin.

FieldRule
categoryone of brand_voice, audience, hook, banned, win, note
content2 to 1,000 characters
metadataan optional object of your own keys

Pin a confirmed fact. Do not pin a guess. The agent treats a pin as a rule of your brand.

CategoryUse it for
brand_voicehow the brand talks
audiencewho the ads talk to
hookan opening that works
banneda word or a claim that the agent must not use
winan ad or an angle that performed
noteanything else that the agent must remember

The cap is 50 for each category

A category holds 50 rows. roasOS drops the oldest pin of that category when you pass the cap. It never drops the marketing brief.

The brief rides the note category as one row, so a workspace with a brief keeps 49 pins in note and 50 in each other category.

roasOS writes your new pin first and culls the category after it. The answer is therefore the same 201 whether a cull ran or not, and your new pin is never the row that goes.

The cap bounds what the agent reads in one turn. Keep the pins short and true, and the cap is never a problem.

Unpin a fact

curl -X DELETE https://api.roasos.com/v1/memory/pins/b2a1c0de-... \
  -H "Authorization: Bearer roasos_sk_..."
{ "data": { "id": "b2a1c0de-...", "resource": "memory_pin", "deleted": true } }

roasOS scopes the id to your workspace before it deletes. An id of another workspace is 404 memory_pin_not_found, and no row is touched.

The marketing brief is not a pin, so this door cannot delete it. The id of the brief row answers the same 404. Replace the brief on PUT /v1/memory/brief instead.

A connected model cannot unpin

roasos_delete_memory_pin is declared and it is never bound on the MCP server. A connected model may therefore write memory, and it can never erase memory. Every delete door of roasOS follows this rule. Delete a pin over REST, with your own key.

The marketing brief

The brief is one structured record for each workspace, so its path names no id either.

Read the brief

curl https://api.roasos.com/v1/memory/brief \
  -H "Authorization: Bearer roasos_sk_..."
{
  "data": {
    "schema_version": "marketing-brief@1",
    "kind": "marketing_brief",
    "brief": {
      "personas": ["women 25 to 40 who buy skincare in a pharmacy"],
      "objections": [
        { "objection": "It is too expensive.", "response": "One bottle lasts eight weeks." }
      ],
      "words_to_use": ["barrier repair", "fragrance free"],
      "voice_notes": "Direct and warm. Short sentences. No hype."
    },
    "updated_at": "2026-08-21T10:24:00.000Z"
  }
}

A workspace that wrote no brief reads as an empty brief object and updated_at: null. That is not an error.

Replace the brief

curl -X PUT https://api.roasos.com/v1/memory/brief \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "personas": ["women 25 to 40 who buy skincare in a pharmacy"],
    "words_to_use": ["barrier repair", "fragrance free"],
    "voice_notes": "Direct and warm. Short sentences. No hype."
  }'

PUT replaces the whole brief. A field that you omit is cleared. Send at least one field: an empty body is 400 validation_error, so a PUT can never empty the brief by accident.

The in-app agent behaves differently on purpose. Its own tool merge-patches the brief, one field at a time, while it talks to a person. This door replaces the brief, because a public write that merged would make "clear this field" impossible. Read the brief first, change the object, then send it back.

The fields

FieldRule
personas8 entries at most, each 2 to 300 characters
jtbd_forcespush, pull, habit and anxiety, each 2 to 500 characters
objections10 entries at most, each with objection and response
anti_persona2 to 500 characters: who the ads must not talk to
words_to_use30 entries at most, each 1 to 120 characters
words_to_avoid30 entries at most, each 1 to 120 characters
proof_points12 entries at most, each with claim and evidence
voice_notes2 to 800 characters

An unknown field is 400 validation_error. Use the words of your customers. Send confirmed values only.

Errors

CodeStatusCause
validation_error400the body or a query value breaks a rule above
invalid_cursor400the cursor value is not one that roasOS gave you
memory_pin_not_found404your workspace has no pin with that id
memory_store_unavailable503the store did not answer, or the answer did not match the contract

memory_store_unavailable publishes retryable: false. It is a real published shape and not a transient, so a retry loop is the wrong answer to it.

From an agent

ToolOperation
roasos_list_memory_pinsGET /v1/memory/pins
roasos_create_memory_pinPOST /v1/memory/pins
roasos_get_marketing_briefGET /v1/memory/brief
roasos_update_marketing_briefPUT /v1/memory/brief

Refer to Connect an AI agent (MCP).

Next

On this page