Agent Preferences
Agents -- Preferences are per-agent key/value rows for learned and explicit user preferences (timezone, tone, sign-off, formatting). Distinct from Memory -- preferences are key-addressable and idempotent on (owner, key). New values for an existing key are archived in a supersedes chain.
All endpoints require authentication via X-API-Key header and the appropriate scope.
Preference Object
{
"_id": "pref_abc123",
"key": "preferred_sign_off",
"value": "Cheers",
"category": "communication",
"preferenceSource": "explicit",
"confidence": 1.0,
"evidence": "User said 'sign me Cheers' on 2026-05-14.",
"learnedAt": "2026-05-15T10:00:00Z",
"lastUsedAt": "2026-05-17T09:55:00Z",
"supersedes": null,
"supersededBy": null,
"ownerId": "usr_mn0",
"organizationId": "org_acme",
"sharedWith": [],
"isPublic": false,
"source": "user",
"tags": ["email", "tone"],
"linkedIds": ["wf_iris"],
"currentVersion": 1,
"createdAt": "2026-05-15T10:00:00Z",
"updatedAt": "2026-05-17T10:00:00Z"
}
preferenceSource is one of: explicit, learned, inferred, imported, manual.
GET /api/v1/primitives/preferences
List preferences visible to the caller. Filters by preferenceSource, category, tags, linkedIds, and q.
Scope: preferences:read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
preferenceSource | string | No | Filter by source kind: explicit, learned, inferred, imported, manual. Any other value returns 400 validation-error |
category | string | No | Filter by category (exact match) |
tags | string | No | Comma-separated tag list; a preference must carry all of them |
linkedIds | string | No | Comma-separated agent IDs; a preference must belong to all of them |
q | string | No | Case-insensitive substring match on key, value, category, and tags |
includeSuperseded | boolean | No | true also returns rows that a newer value has replaced (default false) |
limit | integer | No | Page size (default 50, max 200) |
cursor | string | No | meta.nextCursor of the previous page; omit for the first page |
POST /api/v1/primitives/preferences
Upsert by (owner, key). Idempotent: writing the same key+value twice is a no-op. Writing a new value for an existing key archives the previous row in the supersedes chain and points supersededBy at the new row.
Scope: preferences:write
Request Body
{
"key": "preferred_sign_off",
"value": "Cheers",
"category": "communication",
"preferenceSource": "explicit",
"confidence": 1.0,
"evidence": "User said 'sign me Cheers' on 2026-05-14.",
"tags": ["email", "tone"],
"linkedIds": ["wf_iris"]
}
Response 201 Created
{
"data": { "_id": "pref_abc123" },
"meta": { "requestId": "req_abc123" }
}
GET /api/v1/primitives/preferences/:id
Fetch a preference by id.
Scope: preferences:read
GET /api/v1/primitives/preferences/by-key/:key
Look up the caller's current preference for a key (the row that is not yet superseded).
Scope: preferences:read
PATCH /api/v1/primitives/preferences/:id
Update a preference. Only the fields you send change. A new value supersedes the current one, as POST /primitives/preferences with its key does: the old value is kept in the supersedes chain, the response is the new value's row (with a new _id), and fields you leave out carry forward. Other changes edit the row in place. A value that has already been superseded refuses a new value (preference.superseded); change the current one.
Scope: preferences:write
Request Body -- any subset of editable fields:
{
"value": "Best",
"category": "communication",
"confidence": 0.9
}
linkedIds replaces the agents and pools the preference belongs to.
Response 200 OK -- the preference as saved (the new row when the value changed).
DELETE /api/v1/primitives/preferences/:id
Delete a preference by id. The current value goes with its earlier values (the key is forgotten). A superseded value goes alone, and the values either side of it are linked to each other.
Scope: preferences:write
Response 204 No Content
POST /api/v1/primitives/preferences/forget
Forget a preference by key: removes the caller's current value for that key and its earlier values.
Scope: preferences:write
Request Body
{ "key": "preferred_sign_off" }
Sharing
Who can reach a preference is the platform's one sharing shape: its owner, its members
(role editor can use and change it, user can only use it) and its visibility
(public: every user can find and use it; in a multi-tenant deployment, every user of
its organization). Changing it stays with its owner and editors.
| Method | Path | Does | Scope |
|---|---|---|---|
| GET | /api/v1/primitives/preferences/:id/permissions | Owner, members (userId, role) and visibility | preferences:read |
| POST | /api/v1/primitives/preferences/:id/permissions/members | Share with a user: { "userId", "role": "editor" | "user" } | preferences:write |
| DELETE | /api/v1/primitives/preferences/:preferenceId/permissions/members/:userId | Stop sharing with a user | preferences:write |
| PATCH | /api/v1/primitives/preferences/:id/permissions | { "visibility": "public" | "private" } | preferences:write |
Each, except a member's removal (204), answers the permissions as they are now:
{
"data": {
"resourceId": "<preference id>",
"owner": "<user id>",
"members": [{ "userId": "<user id>", "role": "user" }],
"visibility": "private"
},
"meta": { "requestId": "req_abc123" }
}
POST /api/v1/primitives/preferences/search
The preferences relevant to a turn (by confidence, category and the turn's words in a key, value or evidence), and the markdown block an agent puts in its system prompt.
Scope: preferences:read
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
userTurn | string | No | The current turn |
category | string | No | Only this category (for example communication) |
limit | integer | No | How many (default 20, at most 100) |
tags | string | No | Comma-separated: only preferences with all of them |
linkedIds | string[] | No | The calling agent's id and pools: preferences in any of them |
Response 200 OK: { preferences, contextBlock }.
Python SDK
from strongly import Strongly
client = Strongly()
# Upsert by key -- idempotent on (owner, scope, key).
client.preferences.set(
key="preferred_sign_off",
value="Cheers",
preference_source="explicit",
)
# Read.
current = client.preferences.by_key("preferred_sign_off")
# Forget the current row for a key.
client.preferences.forget("preferred_sign_off")
Full client surface: list, create, set, retrieve, by_key, update, delete, forget, share, unshare, toggle_public.
See also
- Library tool families -- agent-callable
preference_*tools - Memory API -- free-form, ranked-by-relevance content
- Agents API