Tone collections

A tone collection groups tones into a named bundle (e.g. "Friendly voices"). Collections are non-versioned, scoped per building block, and reachable through client.tones.collections. The platform exposes them at /api/v1/tones/collections; visibility is set inline via update(). Membership is managed by the uniform listItems(id) / setItems(id, itemIds) pair.

Breaking change in 0.2.0 The previous flat client.collections.* namespace has been removed - it pointed at /api/v1/collections/*, which does not exist on the platform and always returned 404. Use client.tones.collections for tone collections and client.constraints.collections for constraint collections. See the project CHANGELOG for the full migration note.
Methods

list

client.tones.collections.list(params?: ListParams): Promise<Page<CollectionSummary>>

Each CollectionSummary carries id, name, description, tags, itemCount, and the standard engagement fields.

List public tone collections
const page = await client.tones.collections.list({ scope: "public", pageSize: 12 });
return {
  total: page.total,
  items: page.items.map((c) => ({
    id: c.id,
    name: c.name,
    itemCount: c.itemCount,
    tags: c.tags,
  })),
};

listAll

client.tones.collections.listAll(params?: ListParams): AsyncIterable<CollectionSummary>

Iterate the public tone collections feed
const names = [];
for await (const collection of client.tones.collections.listAll({ scope: "public", pageSize: 12 })) {
  names.push(collection.name);
  if (names.length >= 12) break;
}
return names;

get

client.tones.collections.get(id: string): Promise<ToneCollection>

Detail responses include the tone members inline as items: an array of ToneCollectionItem objects, each carrying the join-row id, the underlying toneId, and the tone label.

Fetch a public tone collection
const page = await client.tones.collections.list({ scope: "public", pageSize: 6 });
const first = page.items[0];
if (!first) return { empty: true };

const collection = await client.tones.collections.get(first.id);
return {
  id: collection.id,
  name: collection.name,
  description: collection.description,
  tags: collection.tags,
  itemCount: collection.items.length,
  items: collection.items.map((it) => ({ toneId: it.toneId, label: it.label })),
};

create

client.tones.collections.create(input: CollectionCreateInput): Promise<CollectionCreateResponse>

Pass itemIds to seed the collection with tones. The previous separate toneIds field has been removed; itemIds is the single, uniform input across both tone and constraint collections.

⚠️ Writes to your account. Cleans up after itself.
Create then delete
const created = await client.tones.collections.create({
  name: `docs-demo-tone-coll-${Date.now()}`,
  description: "Created from the docs site.",
  isPublic: false,
  tags: ["demo"],
});
await client.tones.collections.delete(created.id);
return created;

update

client.tones.collections.update(id: string, input: CollectionUpdateInput): Promise<void>

Updates name, description, tags, or visibility. There is no separate visibility endpoint - pass isPublic here.

⚠️ Writes to your account. Cleans up after itself.
Create, update, clean up
const created = await client.tones.collections.create({
  name: `docs-demo-${Date.now()}`,
  description: "original",
  isPublic: false,
});
await client.tones.collections.update(created.id, {
  name: `docs-demo-updated-${Date.now()}`,
  description: "updated",
  tags: ["updated"],
  isPublic: true,
});
const detail = await client.tones.collections.get(created.id);
await client.tones.collections.delete(created.id);
return {
  name: detail.name,
  description: detail.description,
  tags: detail.tags,
  isPublic: detail.isPublic,
};

delete

client.tones.collections.delete(id: string): Promise<void>

⚠️ Writes to your account. Cleans up after itself.
Create then delete
const created = await client.tones.collections.create({ name: `docs-demo-${Date.now()}` });
await client.tones.collections.delete(created.id);
try {
  await client.tones.collections.get(created.id);
  return { deleted: false };
} catch (err) {
  if (err instanceof PromptyNotFoundError) return { deleted: true };
  throw err;
}

vote

client.tones.collections.vote(id: string, value: 1 | -1): Promise<void>

⚠️ Writes to your account. Cleans up after itself.
Vote on your own tone collection
const created = await client.tones.collections.create({ name: `docs-demo-vote-${Date.now()}` });
await client.tones.collections.vote(created.id, 1);
const detail = await client.tones.collections.get(created.id);
await client.tones.collections.delete(created.id);
return { upvotes: detail.upvotes, userVote: detail.userVote };

unvote

client.tones.collections.unvote(id: string): Promise<void>

⚠️ Writes to your account. Cleans up after itself.
Vote, unvote, clean up
const created = await client.tones.collections.create({ name: `docs-demo-unvote-${Date.now()}` });
await client.tones.collections.vote(created.id, -1);
await client.tones.collections.unvote(created.id);
const detail = await client.tones.collections.get(created.id);
await client.tones.collections.delete(created.id);
return { downvotes: detail.downvotes, userVote: detail.userVote };

toggleFavorite

client.tones.collections.toggleFavorite(id: string): Promise<{ favorited: boolean }>

⚠️ Writes to your account. Cleans up after itself.
Toggle on, off, clean up
const created = await client.tones.collections.create({ name: `docs-demo-fav-${Date.now()}` });
const a = await client.tones.collections.toggleFavorite(created.id);
const b = await client.tones.collections.toggleFavorite(created.id);
await client.tones.collections.delete(created.id);
return { firstToggle: a.favorited, secondToggle: b.favorited };

listItems

client.tones.collections.listItems(id: string): Promise<readonly ToneCollectionItem[]>

Returns the tones currently in the collection. Each item carries the join-row id, the underlying toneId, and the tone label.

⚠️ Writes to your account. Cleans up after itself.
Build a collection with one tone, list it, clean up
const tone = await client.tones.create({
  label: `docs-tone-${Date.now()}`,
  isPublic: false,
});
const collection = await client.tones.collections.create({
  name: `docs-coll-${Date.now()}`,
  isPublic: false,
  itemIds: [tone.id],
});

const items = await client.tones.collections.listItems(collection.id);

await client.tones.collections.delete(collection.id);
await client.tones.delete(tone.id);

return items;

setItems

client.tones.collections.setItems(id: string, itemIds: readonly string[]): Promise<void>

Replaces the entire tone set for the collection - pass an empty array to clear it. This single method replaces the previous setTones on the legacy flat namespace.

⚠️ Writes to your account. Cleans up after itself.
Add, replace, clear, clean up
const t1 = await client.tones.create({ label: `docs-t1-${Date.now()}`, isPublic: false });
const t2 = await client.tones.create({ label: `docs-t2-${Date.now()}`, isPublic: false });
const collection = await client.tones.collections.create({ name: `docs-coll-${Date.now()}`, isPublic: false });

await client.tones.collections.setItems(collection.id, [t1.id]);
const after1 = (await client.tones.collections.listItems(collection.id)).length;

await client.tones.collections.setItems(collection.id, [t1.id, t2.id]);
const after2 = (await client.tones.collections.listItems(collection.id)).length;

await client.tones.collections.setItems(collection.id, []);
const after3 = (await client.tones.collections.listItems(collection.id)).length;

await client.tones.collections.delete(collection.id);
await client.tones.delete(t1.id);
await client.tones.delete(t2.id);

return { afterFirstSet: after1, afterSecondSet: after2, afterClear: after3 };