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.