# Write tools

31 tools that change something. Each wraps exactly one public GraphQL mutation and executes it verbatim — the same input validation, the same `{ entity, userErrors }` payload, and the same side effects a `/api/v2` partner gets.

## Who can call them

Read access alone never grants writes, and there is no feature flag on the write surface. Two things compose: a write-capable credential, and a workspace the credential can resolve to write into.

| Credential | Write capability |
| --- | --- |
| Bearer | Any valid API token is write-capable — parity with `/api/v2`, where any valid bearer may call every public mutation. |
| OAuth | Only if the token carries the optional write scope, granted by the person at consent time. |

A credential without write capability does not see these tools at all: `tools/list` omits them rather than advertising calls that would fail.

> **6 of these are irreversible** — Tools tagged **destructive** hard-delete the entity. Confirm with the person, echoing the entity id back to them, before calling one. Deleting a view or Collection never deletes content — the items survive and lose only that tag or grouping.

Before writing anything, read [Agent conventions](/api/v2/docs/mcp/conventions) — in particular that a non-empty `userErrors` array is a *successful* result reporting that the write did not happen.

## The surface

## addItemToCollections

Wraps [`addItemToCollections`](/api/v2/docs/mutations/add-item-to-collections).

Add an item to one or more Collections in the current workspace. A Collection is a workspace-scoped tag set (a saved multi-select tag) applied to items; adding an item to a Collection tags it, so the item then appears when that Collection is used as a filter.

## removeItemFromCollections

Wraps [`removeItemFromCollections`](/api/v2/docs/mutations/remove-item-from-collections).

Remove an item from one or more Collections in the current workspace. A Collection is a workspace-scoped tag set applied to items; removing an item from a Collection strips that tag from the item. This NEVER deletes the item and NEVER deletes the Collection — it only detaches the tag. Removing an item that is not in the Collection is a no-op.

## uploadItemFromUrl

Wraps [`uploadItemFromUrl`](/api/v2/docs/mutations/upload-item-from-url).

Enqueue a single public social-media post URL (Instagram, TikTok, or YouTube) for import into the current workspace. Once ingested, the post becomes a normal Archive item.

## refetchEngagementBulk

Wraps [`refetchEngagementBulk`](/api/v2/docs/mutations/refetch-engagement-bulk).

Queue an engagement-metrics refresh (likes, comments, shares, views) for up to 1000 items in the current workspace. SPENDS CREDITS: 5 credits per PROCESSABLE item. The upper bound is itemIds.length x 5, but Instagram stories and items already refreshed in the last 24h are SKIPPED and NOT charged, so the ACTUAL spend is processedCount x 5.

## createCollection

Wraps [`createCollection`](/api/v2/docs/mutations/create-collection).

Create a Collection (a saved tag set) in the current workspace. A Collection is a workspace-scoped multi-select tag applied to items; once created you tag items into it with addItemToCollections and filter items by it.

## updateCollection

Wraps [`updateCollection`](/api/v2/docs/mutations/update-collection).

Rename a Collection (a saved tag set) in the current workspace. Rename is the ONLY thing this changes — a Collection's tagged items are untouched.

## deleteCollection

**destructive**

Wraps [`deleteCollection`](/api/v2/docs/mutations/delete-collection).

Delete a Collection (a saved tag set) from the current workspace. This is a HARD, IRREVERSIBLE delete.

## createContentView

Wraps [`createContentView`](/api/v2/docs/mutations/create-content-view).

Create a saved content (media deck) view in the current workspace. A content view stores a filter set once; read its items later with searchItems(presetId:) instead of re-sending filters each call.

## updateContentView

Wraps [`updateContentView`](/api/v2/docs/mutations/update-content-view).

Partial-update a saved content (media deck) view in the current workspace. Only the fields you supply are changed; omitted fields are left untouched. CLONE FIRST when changing `filters`: fetch the view (getContentView(id:)) or a similar one, copy its `filters`, adapt it, and send the WHOLE desired blob — see WHOLESALE REPLACE below.

## deleteContentView

**destructive**

Wraps [`deleteContentView`](/api/v2/docs/mutations/delete-content-view).

Delete a saved content (media deck) view from the current workspace. This is a HARD, IRREVERSIBLE delete.

## createSocialProfileView

Wraps [`createSocialProfileView`](/api/v2/docs/mutations/create-social-profile-view).

Create a saved Social Profile view in the current workspace. A Social Profile view stores a filter set once; read its profiles later with getSocialProfiles(presetId:) instead of re-sending filters each call.

## updateSocialProfileView

Wraps [`updateSocialProfileView`](/api/v2/docs/mutations/update-social-profile-view).

Partial-update a saved Social Profile view in the current workspace. Only the fields you supply are changed; omitted fields are left untouched. CLONE FIRST when changing `filters`: fetch the view (getSocialProfileView(id:)) or a similar one, copy its `filters`, adapt it, and send the WHOLE desired blob — see WHOLESALE REPLACE below.

## deleteSocialProfileView

**destructive**

Wraps [`deleteSocialProfileView`](/api/v2/docs/mutations/delete-social-profile-view).

Delete a saved Social Profile view from the current workspace. This is a HARD, IRREVERSIBLE delete.

## createCreatorView

Wraps [`createCreatorView`](/api/v2/docs/mutations/create-creator-view).

Create a saved Creator View in the current workspace. A Creator View saves a reusable narrowing (customAttributeConditions + sort) once; read its creators later with creators(presetId:). NOTE: the `filters` blob is stored and echoed back but NOT applied on read — creators(presetId:) narrows ONLY by customAttributeConditions + sort (see VIEW FILTERS below). Use customAttributeConditions to actually narrow results.

## updateCreatorView

Wraps [`updateCreatorView`](/api/v2/docs/mutations/update-creator-view).

Partial-update a saved Creator View in the current workspace. Only the fields you supply are changed; omitted fields are left untouched. CLONE FIRST when changing `filters`: fetch the view (getCreatorView(id:)) or a similar one, copy its `filters`, adapt it, and send the WHOLE desired blob — see WHOLESALE REPLACE below.

## deleteCreatorView

**destructive**

Wraps [`deleteCreatorView`](/api/v2/docs/mutations/delete-creator-view).

Delete a saved Creator View from the current workspace. This is a HARD, IRREVERSIBLE delete.

## createViewGroup

Wraps [`createViewGroup`](/api/v2/docs/mutations/create-view-group).

Create a user-created view group in the current workspace. A view group is a workspace-scoped sidebar bucket that Content Views, Social Profile Views, and workspace Creator Views can be moved into (with moveContentViewToGroup / moveSocialProfileViewToGroup / moveCreatorViewToGroup). CAMPAIGN Creator Views are NEVER group members — they are campaign-scoped, not workspace-scoped.

## updateViewGroup

Wraps [`updateViewGroup`](/api/v2/docs/mutations/update-view-group).

Rename a view group in the current workspace. Rename is the ONLY thing this changes — the group's member Content Views / Social Profile Views / Creator Views and their order are untouched. To change membership use moveContentViewToGroup / moveSocialProfileViewToGroup / moveCreatorViewToGroup; to reorder use reorderViewsInGroup.

## deleteViewGroup

**destructive**

Wraps [`deleteViewGroup`](/api/v2/docs/mutations/delete-view-group).

Delete a view group from the current workspace. This is a HARD, IRREVERSIBLE delete.

## moveContentViewToGroup

Wraps [`moveContentViewToGroup`](/api/v2/docs/mutations/move-content-view-to-group).

Move a Content View (a saved media-deck view) into a view group, or remove it from whatever group it is in. Use this tool for CONTENT VIEWS ONLY — views whose ids come from getContentViews / getContentView. To move a Social Profile View (ids from getSocialProfileViews) use moveSocialProfileViewToGroup instead.

## moveSocialProfileViewToGroup

Wraps [`moveSocialProfileViewToGroup`](/api/v2/docs/mutations/move-social-profile-view-to-group).

Move a Social Profile View (a saved social-profiles view) into a view group, or remove it from whatever group it is in. Use this tool for SOCIAL PROFILE VIEWS ONLY — views whose ids come from getSocialProfileViews / getSocialProfileView. To move a Content View (ids from getContentViews) use moveContentViewToGroup instead.

## moveCreatorViewToGroup

Wraps [`moveCreatorViewToGroup`](/api/v2/docs/mutations/move-creator-view-to-group).

Move a Creator View (a saved crm_creators view) into a view group, or remove it from whatever group it is in. Use this tool for CREATOR VIEWS ONLY — views whose ids come from getCreatorViews / getCreatorView. To move a Content View (ids from getContentViews) use moveContentViewToGroup instead; to move a Social Profile View use moveSocialProfileViewToGroup.

## moveCollectionToGroup

Wraps [`moveCollectionToGroup`](/api/v2/docs/mutations/move-collection-to-group).

Move a Collection (a saved tag set) into a view group, or remove it from whatever group it is in. Use this tool for COLLECTIONS ONLY — entities whose ids come from getCollections / getCollection. To move a Content View (ids from getContentViews) use moveContentViewToGroup; to move a Creator View use moveCreatorViewToGroup; to move a Social Profile View use moveSocialProfileViewToGroup.

## reorderViewsInGroup

Wraps [`reorderViewsInGroup`](/api/v2/docs/mutations/reorder-views-in-group).

Reorder the member views (Content Views + Social Profile Views + Creator Views) inside a view group. The first id in `viewIds` becomes position 1, the second position 2, and so on.

## createWebhookSubscription

Wraps [`createWebhookSubscription`](/api/v2/docs/mutations/create-webhook-subscription).

Create an outbound-webhook subscription in the current workspace. New items landing in the subscribed content view(s) trigger a signed HTTPS POST to your endpoint.

## updateWebhookSubscription

Wraps [`updateWebhookSubscription`](/api/v2/docs/mutations/update-webhook-subscription).

Partial-update a webhook subscription in the current workspace. Only the fields you supply are changed; omitted fields are left untouched. Pause or resume the subscription via `status`.

## deleteWebhookSubscription

**destructive**

Wraps [`deleteWebhookSubscription`](/api/v2/docs/mutations/delete-webhook-subscription).

Delete a webhook subscription from the current workspace. This is a HARD, IRREVERSIBLE delete.

## rotateWebhookSubscriptionSecret

Wraps [`rotateWebhookSubscriptionSecret`](/api/v2/docs/mutations/rotate-webhook-subscription-secret).

Rotate a webhook subscription's signing secret in the current workspace. Deliveries enter a 24h dual-sign overlap during which they are signed with BOTH the new and previous secret, so the receiver can cut over losslessly.

## sendWebhookTestEvent

**external call**

Wraps [`sendWebhookTestEvent`](/api/v2/docs/mutations/send-webhook-test-event).

Send a signed synthetic ping to a webhook subscription's endpoint and return the SETTLED delivery, so you can confirm the endpoint is reachable and verifies signatures. The ping payload is built SERVER-SIDE — it is never caller-supplied.

## redeliverWebhookDelivery

**external call**

Wraps [`redeliverWebhookDelivery`](/api/v2/docs/mutations/redeliver-webhook-delivery).

Manually replay a terminal FAILED webhook delivery in the current workspace — a dead-letter retry. Grants exactly ONE more attempt (CAS-guarded so concurrent replays can't double-enqueue); keeps the lifetime attemptCount / backoff index, and the settlement recorder re-terminates a re-failure. A replay past the retry ceiling is a one-shot, never an automatic retry storm.

## enableWebhookSubscription

**external call**

Wraps [`enableWebhookSubscription`](/api/v2/docs/mutations/enable-webhook-subscription).

Re-enable a webhook subscription in the current workspace that was AUTO-DISABLED by repeated delivery failures (status DISABLED_BY_FAILURES). Clears the failure counters and, by default, replays the last 24h of failed deliveries one-shot per row (one more attempt each, lifetime attemptCount preserved, no backoff-ladder reset) — a bounded catch-up, never a retry storm.
