Skip to main content

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

ParameterTypeRequiredDescription
preferenceSourcestringNoFilter by source kind: explicit, learned, inferred, imported, manual. Any other value returns 400 validation-error
categorystringNoFilter by category (exact match)
tagsstringNoComma-separated tag list; a preference must carry all of them
linkedIdsstringNoComma-separated agent IDs; a preference must belong to all of them
qstringNoCase-insensitive substring match on key, value, category, and tags
includeSupersededbooleanNotrue also returns rows that a newer value has replaced (default false)
limitintegerNoPage size (default 50, max 200)
cursorstringNometa.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.

MethodPathDoesScope
GET/api/v1/primitives/preferences/:id/permissionsOwner, members (userId, role) and visibilitypreferences:read
POST/api/v1/primitives/preferences/:id/permissions/membersShare with a user: { "userId", "role": "editor" | "user" }preferences:write
DELETE/api/v1/primitives/preferences/:preferenceId/permissions/members/:userIdStop sharing with a userpreferences: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

FieldTypeRequiredDescription
userTurnstringNoThe current turn
categorystringNoOnly this category (for example communication)
limitintegerNoHow many (default 20, at most 100)
tagsstringNoComma-separated: only preferences with all of them
linkedIdsstring[]NoThe 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​