Data: collections and items
Store structured data in collections, create it, search it by text and keep control of it.
A collection is a table of structured workspace data; its records are items. It is the store your Met can read as a data source and that you operate over the API to save, search and update information. Work server-side with your key:
const met = new Met(key, { workspaceId });
Collections
const col = await met.collections.create({ name: 'Preferences' });
await met.collections.list(); // Collection[]
await met.collections.retrieve(col.id);
await met.collections.update(col.id, { name: 'Customer preferences' });
await met.collections.delete(col.id);
create accepts name and an optional folder_id? (scopes collections:read/collections:write). A collection's id is numeric.
Hide a collection from the Met without deleting it: set it to
expose_to_agent: false(defaulttrue). The Met stops seeing it as a data source, but you keep operating it over the API. Re-expose it withupdate(col.id, { expose_to_agent: true }). More context in Mets and their tools.
Items
The first argument to almost everything is the numeric collectionId:
const item = await met.items.create(col.id, { name: 'Ana', channel: 'preferred' });
await met.items.list(col.id); // one page { data, has_more }
for await (const it of met.items.iterate(col.id)) { /* every page */ }
await met.items.retrieve(item.id);
await met.items.update(col.id, item.id, { channel: 'whatsapp' });
await met.items.patchField(col.id, item.id, 'channel', 'email'); // a single field
await met.items.setStatus(item.id, 'archived');
await met.items.delete(item.id);
The item's data lives in item.data. If a field is a formula ("=…"), its resolved result shows up in item.computed[field] — data keeps the raw formula; always write to data. For an item with no collection (at workspace level) use met.items.createOrphan(body).
Search
met.items.search(query) returns an Item[] with the matches across the whole workspace:
const found = await met.items.search('annual discount');
It is lexical text search: it matches on the words that appear in the item's fields. It is not semantic or vector search — there are no embeddings and no ranking by meaning, so search for the literal terms you expect to find. To narrow by another field, filter the result in your code:
const onlyAnas = (await met.items.search('preference'))
.filter((it) => it.data?.contact_id === 42);
Example: memory per customer
A preferences collection, one item per contact, and retrieval by search + filter:
const prefs = await met.collections.create({ name: 'preferences' });
await met.items.create(prefs.id, {
contact_id: 42,
note: 'Prefers being written to in the morning, warm tone.',
});
// Later: retrieve what we know about that contact
const memory = (await met.items.search('prefers'))
.filter((it) => it.data?.contact_id === 42);
Because search is lexical, store in the item's text the words you will later want to find it by.
Folders
Once a workspace goes past ten or so collections, the panel groups them into folders. It organizes the view, not the data: moving a collection to another folder changes none of its items and breaks no references.
const folders = await met.folders.list();
const tree = await met.folders.hierarchy();
list() returns the folders flat; hierarchy() returns them nested, which is what you want in order to paint a tree without rebuilding the parent-child relationship by hand.
const f = await met.folders.create({ name: 'Operations' });
await met.folders.move(f.id, null); // null = root
await met.folders.reorder([f.id, other.id]);
move accepts null as the parent to lift a folder to the root. Order is explicit and persistent: reorder takes the ids in the order you want, and reorderCollectionsInFolder does the same with the collections inside a folder. There is no automatic alphabetical order — if you don't reorder, they stay as they were created.
Slugs: readable URLs instead of ids
An item and a collection have a numeric id, and can also have a slug. It exists so your integration doesn't have to store ids of ours:
await met.slugs.updateCollectionSlug(collection.id, 'invoices');
await met.slugs.updateItemSlug(item.id, 'inv-001');
const col = await met.slugs.resolveCollectionBySlug('invoices');
const inv = await met.slugs.resolveItemBySlug(col.id, 'inv-001');
An item's slug is unique within its collection, not across the workspace: that is why resolveItemBySlug asks for both. A collection's slug is unique across the workspace.
There are two more ways to get to something, and the difference matters:
// By readable path: folder/collection
const r = await met.slugs.resolvePath('operations/invoices');
// By the item's short code (the one the panel shows)
const x = await met.slugs.resolveByCode('AB/12');
resolvePath is for building URLs a human reads and edits. resolveByCode is for the reverse: someone reads you the code they see on screen and you have to find it. The code is generated by Meteor and does not change; the slug is set by you and can change, so don't use it as a stable identifier in your database — that is what the id, or the code, is for.
Collections as operational memory
Collections can bring together context from contacts, processes and operations so Mets can consult and update it within their configured permissions. This makes them the structured memory of Meteor's Company Brain, rather than an isolated repository that depends on manual searches.