Inventory
Per-user structured items the agent can read and reason about — medications, holdings, pets, goals — stored against a shared KB schema.
Inventory is the place to store structured per-user data the agent should know about. Each item belongs to a single agent × user pair and follows a schema defined in your Knowledge Base, so the agent always has typed, queryable data rather than free-form text. When the agent adds an item it searches the KB by description to resolve and link the right node automatically.
What you can build with it
- Medication adherence — track each drug, dose, and schedule per user (pairs with Scheduled Reminders)
- Portfolio / holdings — stocks, crypto, collectibles with market-value joins via the KB
- Pet care — pets per user, feeding, vet, and growth tracking
- Goal tracking — user-defined goals with progress state
- Plants / hobbies — anything that follows a "user has N things of type T" pattern
Quickstart
Add a medication to a user's inventory. The response includes an inventory_item_id (and the backward-compatible fact_id alias) you can use for direct updates or deletes later.
import { Sonzai } from "@sonzai-labs/agents";
const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
// Preferred: dedicated create route — no action field needed
const result = await client.agents.inventory.create("agent_abc", "user_123", {
item_type: "medication",
label: "Metformin",
description: "Metformin 500mg — biguanide for blood sugar control",
properties: {
dose_mg: 500,
frequency: "twice daily",
with_food: true,
},
});
console.log(result.inventory_item_id); // "inv_01HX..." (preferred)
console.log(result.fact_id); // "fact_01HX..." (backward compat alias)
console.log(result.status); // "added" | "disambiguation_needed"
// Alternative: explicit-action route (still supported for backward compat)
// await client.agents.inventory.update("agent_abc", "user_123", { action: "add", item_type: "medication", ... });KB resolution on add
When action is "add", the platform performs a natural-language search of the KB using description. If exactly one node matches, the item is linked automatically and the response includes kb_resolution. If there are multiple close matches, the response returns status: "disambiguation_needed" and a candidates list — surface these to the user or pick the best kb_node_id and re-submit.
label vs description
label is an optional short display name shown in dashboards and agent tool calls (e.g. "Metformin"). description is the longer text the platform uses for KB natural-language search (e.g. "Metformin 500mg — biguanide for blood sugar control"). If label is omitted, the platform falls back to the first segment of description for display purposes.
Core concepts
- Items belong to users — every item is scoped to
agent_id × user_id; no item is shared across users - Schema-driven shape —
item_typereferences a KB schema that defines the valid property fields; the platform validates writes against it - Two write paths for adding items — use
inventory.create({...})(dedicated route, no action field) for cleaner code when you specifically want to add; useinventory.update({action: "add", ...})(explicit-action route) when you handle add/update/remove through a single call site. Both hit equivalent server logic. labelvsdescription—labelis a short display name for dashboards and agent UI (e.g."Ibuprofen");descriptionis the longer text the KB search uses to resolve the right node (e.g."anti-inflammatory pain reliever, 400mg"). Both are optional but providing both gives the clearest results.- KB resolution — on add, Sonzai searches the KB by
description; on ambiguous matches it returnscandidatesandstatus: "disambiguation_needed"so you can resolve before committing - Query modes —
"list"returns raw items,"value"joins with live KB market data and computesgain_loss,"aggregate"returns totals and grouped sums without listing every item
Full API
| Method | Description |
|---|---|
inventory.create(agentId, userId, { item_type, label?, description?, kb_node_id?, properties?, project_id? }) | Preferred add path. Dedicated create endpoint — no action field needed. Returns InventoryUpdateResponse with inventory_item_id. |
inventory.update(agentId, userId, { action, item_type, label?, description?, kb_node_id?, properties?, project_id? }) | Explicit-action path. action is "add", "update", or "remove". Use when you route all three write types through one call site. |
inventory.query(agentId, userId, { mode, item_type?, project_id?, filters?, sort_by?, sort_order?, aggregations?, limit?, offset?, cursor? }) | Query items in list, value, or aggregate mode. |
inventory.directUpdate(agentId, userId, factId, { properties }) | Update an item's properties by fact_id, bypassing KB re-resolution. |
inventory.directDelete(agentId, userId, factId) | Delete an item by fact_id. |
inventory.batchImport(agentId, userId, { items: [{ item_type, description?, kb_node_id?, properties? }], project_id? }) | Import up to 1,000 items in one call. |
Response shape
InventoryUpdateResponse:
{
"status": "added",
"inventory_item_id": "inv_01HX...",
"fact_id": "fact_01HX...",
"kb_resolution": {
"resolved": true,
"kb_node_id": "node_xyz",
"kb_label": "Metformin 500mg",
"kb_properties": { "drug_class": "biguanide" }
}
}inventory_item_id is the preferred identifier going forward. fact_id is included for backward compatibility — both refer to the same item and are interchangeable in all subsequent API calls (direct update, direct delete, schedule linkage).
When status is "disambiguation_needed", the response includes a candidates array instead of kb_resolution. Re-submit with the chosen kb_node_id set explicitly to bypass the search.
Combines with other features
With Knowledge Base — schemas shape items
The item_type field points to a KB entity schema that defines which properties are valid for that type. Create the schema once; all inventory writes for that type are validated against it.
// 1. Define the schema in the KB once
await client.knowledge.createSchema("proj_abc123", {
entity_type: "medication",
fields: [
{ name: "dose_mg", type: "number", required: true },
{ name: "frequency", type: "string", required: true },
{ name: "with_food", type: "boolean", required: false },
],
});
// 2. Inventory writes for item_type "medication" are now validated
await client.agents.inventory.update("agent_abc", "user_123", {
action: "add",
item_type: "medication", // <-- resolves to the schema above
description: "Metformin 500mg",
properties: { dose_mg: 500, frequency: "twice daily", with_food: true },
});With Scheduled Reminders — live data at fire time
A schedule can reference an inventory_item_id. At each fire, the agent reads the item's current properties rather than a snapshot baked into the schedule definition. Updating the item's dosage automatically flows to the next reminder without touching the schedule itself.
// Add the item first
const { fact_id } = await client.agents.inventory.update("agent_abc", "user_123", {
action: "add",
item_type: "medication",
description: "Metformin 500mg",
properties: { dose_mg: 500, frequency: "twice daily" },
});
// Reference it in a schedule — agent reads live properties at each fire
await client.schedules.create("agent_abc", "user_123", {
cadence: {
simple: { frequency: "daily", times: ["08:00", "20:00"] },
timezone: "America/New_York",
},
intent: "remind the user to take their medication",
inventory_item_id: fact_id,
});With Memory — inventory state in conversation context
During a conversation the agent can query the user's inventory to answer questions like "what medications am I taking?" directly. Inventory writes also generate memory facts that surface in future sessions, so the agent can reference holdings and items across conversations without a manual query.
// Agent answers from inventory mid-conversation
for await (const event of client.agents.chatStream("agent_abc", {
userId: "user_123",
messages: [{ role: "user", content: "What medications am I on?" }],
})) {
// The agent calls sonzai_inventory internally to fetch the user's items
// and answers from live data — no extra code needed.
process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}Tutorials
- Resource Inventory + Knowledge Base — full walkthrough with schema setup, upsert, bulk import, and portfolio queries
- Medication Reminders — worked example combining Inventory + Scheduled Reminders + Memory
Next steps
- Knowledge Base — the schema backbone that shapes inventory items
- Scheduled Reminders — live inventory data injection at fire time
- Memory — how inventory writes surface in chat context
Organization-Global Knowledge Base
One organization-wide knowledge scope that every project under the tenant can read from. Use it for policies, shared lore, playbooks, or facts that belong to the whole organization rather than a single project.
Priming
Bootstrap agents with user context — display names, demographic facts, chat transcripts from legacy systems, or bulk imports from CSV/CRM.