Integration details
Description
Archive helps teams search workspace social content and creators, inspect campaigns, saved views, collections, operations, and webhooks, and manage those workspace records through ChatGPT.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Influencer & Creator Discovery
- Secondary Subcategories
- None listed
- Brand
- Archive
- Access
- Account required
- First tracked
- 2026-09-13
- Tool count
- 64
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Influencer & Creator Discovery
View Category64 tools agents can invoke
PURPOSE: 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. INPUT: - itemId (required string): the Archive item id to tag. - collectionIds (array of strings, default []): Collection ids to add the item to. - collectionNames (array of strings, default []): Collection names to add the item to, matched case-sensitively within the workspace. - autoCreate (boolean, default false): when true, a name in `collectionNames` that does not match an existing Collection creates a new Collection; when false, unmatched names are dropped and, if NO collection resolves at all, the call returns a "No valid collections found" userError. You MUST provide at least one of `collectionIds` or `collectionNames`. Providing neither returns a userErrors entry ("Provide 'collectionIds' or 'collectionNames'") and makes no change. RESOLVING IDS vs NAMES: prefer ids. Resolve Collection ids with getCollections first, then pass them as `collectionIds`. Use the ids returned by getCollections for THIS workspace: several id forms are accepted depending on how the workspace stores Collections. An id from another workspace, or one this workspace cannot write through this API, rejects the WHOLE call (all-or-nothing) with a userErrors entry naming the offending id under `collectionIds`. An id that exists in the workspace but is not writable here reports "exists in this workspace but is not writable through this API" — re-resolve via getCollections and retry with the id it returns. AUTO_CREATE: use `autoCreate: true` only when creating a Collection is intended. It creates the new Collection in this workspace's own Collection storage. A blank name is rejected. Resolve names via a read tool first when you mean to reference an existing Collection — a typo with `autoCreate: true` silently creates an unwanted Collection. OUTPUT: { item: { id, customAttributes } | null, userErrors: [{ field, message }] }. `item` is the item as stored after the write. In workspaces whose Collections use the older storage, `item.customAttributes.collections` does NOT reflect Collection membership and is returned unchanged — use getCollection(id:).itemCount to confirm the write. In preset-backed workspaces that array holds the item's Collection ids after the write. `userErrors[].field` is an array of path segments (e.g. ["collectionIds"]). VERIFY AFTER WRITE: PRIMARY (works in every workspace): getCollection(id: "<collection-id>") — its `itemCount` reflects the change immediately. Default to this check. SECONDARY, workspaces using the OLDER Collection storage only: searchItems(filter: { collectionsIds: ["<collection-id>"] }) with a Collection id from getCollections, and check the item is present. Search results lag a write by up to about a minute, so this is confirmation, not the immediate check. SECONDARY, preset-backed workspaces: searchItems(customAttributeConditions: [ { field: "collections", operator: "CONTAINS", type: "MULTIPLE_SELECT_V2", value: ["<collection-id>"] } ]) and check the item is present. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
addItemToCollections
PURPOSE: 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. INPUT: - name (required string): the display name for the new Collection. Names are UNIQUE within the workspace. NAME UNIQUENESS: if a Collection with the given name already exists, the call returns a duplicate-name userError (field ["input","name"]) and creates NOTHING. Do NOT invent a name variant ("Summer 2", "Summer_new") to work around it — REUSE the existing Collection instead: look it up with getCollections, take its id, and proceed with that id. AVAILABILITY: creating a Collection is only possible in workspaces whose Collections use the standard storage. In a workspace that stores Collections in an older format the call returns a schema-missing userError and creates nothing; there, create the Collection by tagging an item into it directly — addItemToCollections with collectionNames plus autoCreate: true creates the named Collection as part of the tagging call. OUTPUT: { collection: { id, name, itemCount } | null, userErrors: [{ field, message }] }. `collection.id` is the Collection's id in this workspace — pass it to addItemToCollections / removeItemFromCollections to tag items, and to searchItems(presetId:) or getCollection(id:) to read it. `collection` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","name"]). VERIFY AFTER WRITE: confirm the Collection landed with getCollection(id:) using the returned `collection.id`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createCollection
PURPOSE: 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. CLONE FIRST (strongly preferred): the `filters` blob is opaque and easy to get subtly wrong. Before composing filters from scratch, fetch an existing content view with getContentViews (or getContentView(id:)), copy its `filters` value, and adapt it. A cloned-and-tweaked blob renders correctly in the app; a hand-built one may look successful here yet render broken. Only build filters from scratch for simple views, using the VIEW FILTERS contract below. INPUT: - name (required string): the display name. Names are NOT required to be unique — two content views may share a name. - filters (required object): the opaque filter blob (see VIEW FILTERS below). Pass {} for a view with no filters (all items). - customAttributeConditions (optional array): narrows the filter set (see CUSTOM ATTRIBUTE FILTERS below). Defaults to []. - sort (optional array): sort directives, e.g. [{ "field": "taken_at", "direction": "desc" }]. Defaults to []. - showReportingStats (optional boolean): whether reporting stats surface in the UI. Defaults to true. OUTPUT: { contentView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null, userErrors: [{ field, message }] }. `contentView.id` is the view's canonical FilterPreset UUID — pass it to searchItems(presetId:) to read its items, or to getContentView(id:) to read it back. `contentView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","name"]). VERIFY AFTER WRITE: confirm the view landed with getContentView(id:) using the returned `contentView.id`. VIEW FILTERS (the `filters` blob): `filters` is a JSON object mapping filter keys to values, stored verbatim. It is OPAQUE and unvalidated on write — an invalid blob is accepted but may render the view broken in the app. STRONGLY PREFER cloning an existing view's `filters` (fetch it with the matching get* tool) over composing from scratch. Pass {} for "no filters". CONTENT-VIEW keys (createContentView / updateContentView) — applied when the view is read via items(presetId:) / searchItems(presetId:): - item_types : array of item types, e.g. ["POST","REEL","STORY","SHORT"]. - provider : one of "INSTAGRAM","TIKTOK","YOUTUBE". - content_types : array of media types, e.g. ["IMAGE","VIDEO"]. - taken_at : publication-date range { "from": ISO8601, "to": ISO8601 }. - virality_score : array of "VIRAL","HIGH","MEDIUM","LOW". - tags_names : array of hashtag / mention tag names. - campaigns_ids : array of campaign ids. - collections_ids : array of Collection ids (as returned by getCollections). - social_profile_ids : array of social profile ids. - account_names : array of social profile handles. - followers_count : follower-count range { "from": int, "to": int }. SOCIAL-PROFILE-VIEW `filters` (createSocialProfileView / updateSocialProfileView): the blob is stored and echoed back by the get* tools, but it is NOT applied when reading profiles — socialProfiles(presetId:) / getSocialProfiles(presetId:) filter ONLY by the view's customAttributeConditions and sort. To make a Social Profile View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. CREATOR-VIEW `filters` (createCreatorView / updateCreatorView): same caveat — the blob is stored and echoed back by the get* tools, but it is NOT applied when reading creators. creators(presetId:) / searchCreators(presetId:) narrow ONLY by the view's customAttributeConditions and sort. To make a Creator View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. Example content-view filters: { "item_types": ["REEL"], "provider": "INSTAGRAM", "taken_at": { "from": "2024-01-01", "to": "2024-12-31" } }. For anything not listed above (super_search, location filters, contract-status filters, and any key you are unsure of), CLONE an existing view's `filters` — do NOT guess key names. CUSTOM ATTRIBUTE FILTERS (customAttributeConditions): Filter by the workspace's user-defined custom fields. An array of { field, operator, type, value } objects; multiple entries are AND-ed together. Step 1 - discover fields: call getCustomAttributeSchemas(entity: ITEM | CREATOR). Each schema returns `key` (use as `field`), `type` (use as `type`), and `options: [{ id, name }]` for select fields (use an option `id` as `value`). Step 2 - build each condition: - field : the schema `key` (e.g. "sentiment", "links"). - type : the schema `type`, UPPERCASE - one of TEXT, EMAIL, PHONE, URL, NUMBER, BOOLEAN, DATE, DATETIME, SINGLE_SELECT_V2, SINGLE_SELECT_V3, MULTIPLE_SELECT, MULTIPLE_SELECT_V2, TEXT_LIST, NUMBER_LIST, DATE_LIST, DATETIME_LIST, BOOLEAN_LIST. - operator : UPPERCASE; the valid set depends on the field's type group (see below). - value : depends on `type`: * TEXT / EMAIL / PHONE / URL -> a string. * SINGLE_SELECT_V2 / SINGLE_SELECT_V3 -> the chosen option `id` (UUID). * NUMBER -> a number; BETWEEN takes { from, to }. * DATE / DATETIME -> ISO-8601 string; BETWEEN takes { from, to }. * BOOLEAN -> true / false. * MULTIPLE_SELECT / MULTIPLE_SELECT_V2 -> array of option `id`s. * TEXT_LIST / NUMBER_LIST / DATE_LIST / ... -> array of values. * IS_EMPTY / IS_NOT_EMPTY -> value is ignored; pass null. Operators by type group (passing an operator outside its group is rejected with a validation error naming the field; a SHIPPING_ADDRESS-typed condition is always rejected the same way — that is the SHIPPING_ADDRESS *type*, distinct from the `shipping_address` *field* whose stored-only matching semantics are noted below): - Text & single-select (TEXT, EMAIL, PHONE, URL, SINGLE_SELECT_V2, SINGLE_SELECT_V3): IS, IS_NOT, CONTAINS, DOES_NOT_CONTAIN, STARTS_WITH, ENDS_WITH, IS_EMPTY, IS_NOT_EMPTY. (IS / IS_NOT are exact match - for a select field, value is the option id.) - Number (NUMBER): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY. - Date (DATE, DATETIME): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY, IS_RELATIVE_TO_TODAY. IS_RELATIVE_TO_TODAY value: { relation: "past"|"this"|"next", period: "day"|"week"|"month"|"year" } (offset optional); an empty or unrecognized value makes the condition a no-op. - Boolean (BOOLEAN): IS (value true / false). - Multi-value (MULTIPLE_SELECT, MULTIPLE_SELECT_V2, and every *_LIST type): CONTAINS (matches ANY of the values, OR), CONTAINS_ALL (must contain ALL, AND), DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY. NOTE: two schemas are stored but NOT indexed for filtering — the AI free-text summary (post_summary) and shipping_address. For post_summary/shipping_address: positive operators (IS, CONTAINS, STARTS_WITH, etc.) match NOTHING; negated operators (IS_NOT, DOES_NOT_CONTAIN, IS_EMPTY) match EVERY record — do not filter on these fields; read their values from each item's `customAttributes` instead. NOTE: silently-ignored conditions — on items, conditions on `labels` and `post_date` are ignored (no error, no filtering effect); on creators, conditions on `full_name` may be ignored (feature-flag-gated). Examples: [{ "field": "sentiment", "operator": "IS", "type": "SINGLE_SELECT_V2", "value": "<option-uuid>" }] [{ "field": "links", "operator": "CONTAINS", "type": "TEXT_LIST", "value": ["https://example.com/promo"] }] [{ "field": "lead_score", "operator": "BETWEEN", "type": "NUMBER", "value": { "from": 10, "to": 100 } }] [{ "field": "notes", "operator": "IS_NOT_EMPTY", "type": "TEXT", "value": null }] WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createContentView
PURPOSE: 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. CLONE FIRST (strongly preferred): the `filters` blob is opaque and easy to get subtly wrong. Before composing filters from scratch, fetch an existing Creator View with getCreatorViews (or getCreatorView(id:)), copy its `filters` value, and adapt it. A cloned-and-tweaked blob renders correctly in the app; a hand-built one may look successful here yet render broken. Only build filters from scratch for simple views, using the VIEW FILTERS contract below. INPUT: - name (required string): the display name. Names are NOT required to be unique — two Creator Views may share a name. - filters (required object): the opaque filter blob (see VIEW FILTERS below) — stored and echoed back but NOT applied on read. Pass {} unless cloning an existing view verbatim. - customAttributeConditions (optional array): narrows the filter set (see CUSTOM ATTRIBUTE FILTERS below). Defaults to []. - sort (optional array): sort directives. Defaults to []. - showReportingStats (optional boolean): whether reporting stats surface in the UI. Defaults to true. OUTPUT: { creatorView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null, userErrors: [{ field, message }] }. `creatorView.id` is the view's canonical FilterPreset UUID — pass it to creators(presetId:) to read its creators, or to getCreatorView(id:) to read it back. `creatorView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","name"]). VERIFY AFTER WRITE: confirm the view landed with getCreatorView(id:) using the returned `creatorView.id`. VIEW FILTERS (the `filters` blob): `filters` is a JSON object mapping filter keys to values, stored verbatim. It is OPAQUE and unvalidated on write — an invalid blob is accepted but may render the view broken in the app. STRONGLY PREFER cloning an existing view's `filters` (fetch it with the matching get* tool) over composing from scratch. Pass {} for "no filters". CONTENT-VIEW keys (createContentView / updateContentView) — applied when the view is read via items(presetId:) / searchItems(presetId:): - item_types : array of item types, e.g. ["POST","REEL","STORY","SHORT"]. - provider : one of "INSTAGRAM","TIKTOK","YOUTUBE". - content_types : array of media types, e.g. ["IMAGE","VIDEO"]. - taken_at : publication-date range { "from": ISO8601, "to": ISO8601 }. - virality_score : array of "VIRAL","HIGH","MEDIUM","LOW". - tags_names : array of hashtag / mention tag names. - campaigns_ids : array of campaign ids. - collections_ids : array of Collection ids (as returned by getCollections). - social_profile_ids : array of social profile ids. - account_names : array of social profile handles. - followers_count : follower-count range { "from": int, "to": int }. SOCIAL-PROFILE-VIEW `filters` (createSocialProfileView / updateSocialProfileView): the blob is stored and echoed back by the get* tools, but it is NOT applied when reading profiles — socialProfiles(presetId:) / getSocialProfiles(presetId:) filter ONLY by the view's customAttributeConditions and sort. To make a Social Profile View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. CREATOR-VIEW `filters` (createCreatorView / updateCreatorView): same caveat — the blob is stored and echoed back by the get* tools, but it is NOT applied when reading creators. creators(presetId:) / searchCreators(presetId:) narrow ONLY by the view's customAttributeConditions and sort. To make a Creator View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. Example content-view filters: { "item_types": ["REEL"], "provider": "INSTAGRAM", "taken_at": { "from": "2024-01-01", "to": "2024-12-31" } }. For anything not listed above (super_search, location filters, contract-status filters, and any key you are unsure of), CLONE an existing view's `filters` — do NOT guess key names. CUSTOM ATTRIBUTE FILTERS (customAttributeConditions): Filter by the workspace's user-defined custom fields. An array of { field, operator, type, value } objects; multiple entries are AND-ed together. Step 1 - discover fields: call getCustomAttributeSchemas(entity: ITEM | CREATOR). Each schema returns `key` (use as `field`), `type` (use as `type`), and `options: [{ id, name }]` for select fields (use an option `id` as `value`). Step 2 - build each condition: - field : the schema `key` (e.g. "sentiment", "links"). - type : the schema `type`, UPPERCASE - one of TEXT, EMAIL, PHONE, URL, NUMBER, BOOLEAN, DATE, DATETIME, SINGLE_SELECT_V2, SINGLE_SELECT_V3, MULTIPLE_SELECT, MULTIPLE_SELECT_V2, TEXT_LIST, NUMBER_LIST, DATE_LIST, DATETIME_LIST, BOOLEAN_LIST. - operator : UPPERCASE; the valid set depends on the field's type group (see below). - value : depends on `type`: * TEXT / EMAIL / PHONE / URL -> a string. * SINGLE_SELECT_V2 / SINGLE_SELECT_V3 -> the chosen option `id` (UUID). * NUMBER -> a number; BETWEEN takes { from, to }. * DATE / DATETIME -> ISO-8601 string; BETWEEN takes { from, to }. * BOOLEAN -> true / false. * MULTIPLE_SELECT / MULTIPLE_SELECT_V2 -> array of option `id`s. * TEXT_LIST / NUMBER_LIST / DATE_LIST / ... -> array of values. * IS_EMPTY / IS_NOT_EMPTY -> value is ignored; pass null. Operators by type group (passing an operator outside its group is rejected with a validation error naming the field; a SHIPPING_ADDRESS-typed condition is always rejected the same way — that is the SHIPPING_ADDRESS *type*, distinct from the `shipping_address` *field* whose stored-only matching semantics are noted below): - Text & single-select (TEXT, EMAIL, PHONE, URL, SINGLE_SELECT_V2, SINGLE_SELECT_V3): IS, IS_NOT, CONTAINS, DOES_NOT_CONTAIN, STARTS_WITH, ENDS_WITH, IS_EMPTY, IS_NOT_EMPTY. (IS / IS_NOT are exact match - for a select field, value is the option id.) - Number (NUMBER): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY. - Date (DATE, DATETIME): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY, IS_RELATIVE_TO_TODAY. IS_RELATIVE_TO_TODAY value: { relation: "past"|"this"|"next", period: "day"|"week"|"month"|"year" } (offset optional); an empty or unrecognized value makes the condition a no-op. - Boolean (BOOLEAN): IS (value true / false). - Multi-value (MULTIPLE_SELECT, MULTIPLE_SELECT_V2, and every *_LIST type): CONTAINS (matches ANY of the values, OR), CONTAINS_ALL (must contain ALL, AND), DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY. NOTE: two schemas are stored but NOT indexed for filtering — the AI free-text summary (post_summary) and shipping_address. For post_summary/shipping_address: positive operators (IS, CONTAINS, STARTS_WITH, etc.) match NOTHING; negated operators (IS_NOT, DOES_NOT_CONTAIN, IS_EMPTY) match EVERY record — do not filter on these fields; read their values from each item's `customAttributes` instead. NOTE: silently-ignored conditions — on items, conditions on `labels` and `post_date` are ignored (no error, no filtering effect); on creators, conditions on `full_name` may be ignored (feature-flag-gated). Examples: [{ "field": "sentiment", "operator": "IS", "type": "SINGLE_SELECT_V2", "value": "<option-uuid>" }] [{ "field": "links", "operator": "CONTAINS", "type": "TEXT_LIST", "value": ["https://example.com/promo"] }] [{ "field": "lead_score", "operator": "BETWEEN", "type": "NUMBER", "value": { "from": 10, "to": 100 } }] [{ "field": "notes", "operator": "IS_NOT_EMPTY", "type": "TEXT", "value": null }] WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createCreatorView
PURPOSE: 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. CLONE FIRST (strongly preferred): the `filters` blob is opaque and easy to get subtly wrong. Before composing filters from scratch, fetch an existing Social Profile view with getSocialProfileViews (or getSocialProfileView(id:)), copy its `filters` value, and adapt it. A cloned-and-tweaked blob renders correctly in the app; a hand-built one may look successful here yet render broken. Only build filters from scratch for simple views, using the VIEW FILTERS contract below. INPUT: - name (required string): the display name. Names are NOT required to be unique — two Social Profile views may share a name. - filters (required object): the opaque filter blob (see VIEW FILTERS below). Pass {} for a view with no filters (all profiles). - customAttributeConditions (optional array): narrows the filter set (see CUSTOM ATTRIBUTE FILTERS below). Defaults to []. - sort (optional array): sort directives. Defaults to []. - showReportingStats (optional boolean): whether reporting stats surface in the UI. Defaults to true. OUTPUT: { socialProfileView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null, userErrors: [{ field, message }] }. `socialProfileView.id` is the view's canonical FilterPreset UUID — pass it to getSocialProfiles(presetId:) to read its profiles, or to getSocialProfileView(id:) to read it back. `socialProfileView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","name"]). VERIFY AFTER WRITE: confirm the view landed with getSocialProfileView(id:) using the returned `socialProfileView.id`. VIEW FILTERS (the `filters` blob): `filters` is a JSON object mapping filter keys to values, stored verbatim. It is OPAQUE and unvalidated on write — an invalid blob is accepted but may render the view broken in the app. STRONGLY PREFER cloning an existing view's `filters` (fetch it with the matching get* tool) over composing from scratch. Pass {} for "no filters". CONTENT-VIEW keys (createContentView / updateContentView) — applied when the view is read via items(presetId:) / searchItems(presetId:): - item_types : array of item types, e.g. ["POST","REEL","STORY","SHORT"]. - provider : one of "INSTAGRAM","TIKTOK","YOUTUBE". - content_types : array of media types, e.g. ["IMAGE","VIDEO"]. - taken_at : publication-date range { "from": ISO8601, "to": ISO8601 }. - virality_score : array of "VIRAL","HIGH","MEDIUM","LOW". - tags_names : array of hashtag / mention tag names. - campaigns_ids : array of campaign ids. - collections_ids : array of Collection ids (as returned by getCollections). - social_profile_ids : array of social profile ids. - account_names : array of social profile handles. - followers_count : follower-count range { "from": int, "to": int }. SOCIAL-PROFILE-VIEW `filters` (createSocialProfileView / updateSocialProfileView): the blob is stored and echoed back by the get* tools, but it is NOT applied when reading profiles — socialProfiles(presetId:) / getSocialProfiles(presetId:) filter ONLY by the view's customAttributeConditions and sort. To make a Social Profile View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. CREATOR-VIEW `filters` (createCreatorView / updateCreatorView): same caveat — the blob is stored and echoed back by the get* tools, but it is NOT applied when reading creators. creators(presetId:) / searchCreators(presetId:) narrow ONLY by the view's customAttributeConditions and sort. To make a Creator View that actually narrows results, use customAttributeConditions (see CUSTOM ATTRIBUTE CONDITIONS above); pass {} for filters unless cloning an existing view verbatim. Example content-view filters: { "item_types": ["REEL"], "provider": "INSTAGRAM", "taken_at": { "from": "2024-01-01", "to": "2024-12-31" } }. For anything not listed above (super_search, location filters, contract-status filters, and any key you are unsure of), CLONE an existing view's `filters` — do NOT guess key names. CUSTOM ATTRIBUTE FILTERS (customAttributeConditions): Filter by the workspace's user-defined custom fields. An array of { field, operator, type, value } objects; multiple entries are AND-ed together. Step 1 - discover fields: call getCustomAttributeSchemas(entity: ITEM | CREATOR). Each schema returns `key` (use as `field`), `type` (use as `type`), and `options: [{ id, name }]` for select fields (use an option `id` as `value`). Step 2 - build each condition: - field : the schema `key` (e.g. "sentiment", "links"). - type : the schema `type`, UPPERCASE - one of TEXT, EMAIL, PHONE, URL, NUMBER, BOOLEAN, DATE, DATETIME, SINGLE_SELECT_V2, SINGLE_SELECT_V3, MULTIPLE_SELECT, MULTIPLE_SELECT_V2, TEXT_LIST, NUMBER_LIST, DATE_LIST, DATETIME_LIST, BOOLEAN_LIST. - operator : UPPERCASE; the valid set depends on the field's type group (see below). - value : depends on `type`: * TEXT / EMAIL / PHONE / URL -> a string. * SINGLE_SELECT_V2 / SINGLE_SELECT_V3 -> the chosen option `id` (UUID). * NUMBER -> a number; BETWEEN takes { from, to }. * DATE / DATETIME -> ISO-8601 string; BETWEEN takes { from, to }. * BOOLEAN -> true / false. * MULTIPLE_SELECT / MULTIPLE_SELECT_V2 -> array of option `id`s. * TEXT_LIST / NUMBER_LIST / DATE_LIST / ... -> array of values. * IS_EMPTY / IS_NOT_EMPTY -> value is ignored; pass null. Operators by type group (passing an operator outside its group is rejected with a validation error naming the field; a SHIPPING_ADDRESS-typed condition is always rejected the same way — that is the SHIPPING_ADDRESS *type*, distinct from the `shipping_address` *field* whose stored-only matching semantics are noted below): - Text & single-select (TEXT, EMAIL, PHONE, URL, SINGLE_SELECT_V2, SINGLE_SELECT_V3): IS, IS_NOT, CONTAINS, DOES_NOT_CONTAIN, STARTS_WITH, ENDS_WITH, IS_EMPTY, IS_NOT_EMPTY. (IS / IS_NOT are exact match - for a select field, value is the option id.) - Number (NUMBER): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY. - Date (DATE, DATETIME): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY, IS_RELATIVE_TO_TODAY. IS_RELATIVE_TO_TODAY value: { relation: "past"|"this"|"next", period: "day"|"week"|"month"|"year" } (offset optional); an empty or unrecognized value makes the condition a no-op. - Boolean (BOOLEAN): IS (value true / false). - Multi-value (MULTIPLE_SELECT, MULTIPLE_SELECT_V2, and every *_LIST type): CONTAINS (matches ANY of the values, OR), CONTAINS_ALL (must contain ALL, AND), DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY. NOTE: two schemas are stored but NOT indexed for filtering — the AI free-text summary (post_summary) and shipping_address. For post_summary/shipping_address: positive operators (IS, CONTAINS, STARTS_WITH, etc.) match NOTHING; negated operators (IS_NOT, DOES_NOT_CONTAIN, IS_EMPTY) match EVERY record — do not filter on these fields; read their values from each item's `customAttributes` instead. NOTE: silently-ignored conditions — on items, conditions on `labels` and `post_date` are ignored (no error, no filtering effect); on creators, conditions on `full_name` may be ignored (feature-flag-gated). Examples: [{ "field": "sentiment", "operator": "IS", "type": "SINGLE_SELECT_V2", "value": "<option-uuid>" }] [{ "field": "links", "operator": "CONTAINS", "type": "TEXT_LIST", "value": ["https://example.com/promo"] }] [{ "field": "lead_score", "operator": "BETWEEN", "type": "NUMBER", "value": { "from": 10, "to": 100 } }] [{ "field": "notes", "operator": "IS_NOT_EMPTY", "type": "TEXT", "value": null }] WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createSocialProfileView
PURPOSE: 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. INPUT: - name (required string): the display name for the new group. Names are UNIQUE within the workspace. NAME UNIQUENESS: if a group with the given name already exists, the call returns a validation_error userError (field ["input","name"]) and creates NOTHING. Do NOT invent a name variant ("Team 2", "Team_new") to work around it — REUSE the existing group instead: look it up with getViewGroups, take its id, and move views into it with the move tools. OUTPUT: { viewGroup: { id, name, contentViews, socialProfileViews, creatorViews } | null, userErrors: [{ field, message }] }. `viewGroup.id` is the group's canonical UUID — pass it as `groupId` to moveContentViewToGroup / moveSocialProfileViewToGroup / moveCreatorViewToGroup to fill it, to reorderViewsInGroup to order its members, or to getViewGroup(id:) to read it back. A freshly created group has empty `contentViews` / `socialProfileViews` / `creatorViews`. `viewGroup` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","name"]). VERIFY AFTER WRITE: confirm the group landed with getViewGroup(id:) using the returned `viewGroup.id`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createViewGroup
PURPOSE: 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. INPUT: - name (required string): display name for the subscription. - url (required string): the HTTPS endpoint deliveries are POSTed to. Must be HTTPS and pass SSRF validation (no private / link-local hosts) — a bad url returns a userError on ["input","url"]. - viewIds (array of strings): content-view (FilterPreset) UUIDs whose new items trigger deliveries. REQUIRED (at least one, each owned by this workspace — resolve ids with getContentViews first) whenever any eventType is view-scoped, which is every subscribable type in v1; omit it only for a non-view event type (none exist yet). Omitting it with a view-scoped type returns a viewIds userError on ["input","viewIds"]. - eventTypes (optional array of strings): event types to subscribe to. Defaults to ["content_view.item_added"] (the only supported type in v1). - metadata (optional object): opaque metadata echoed back in each delivery payload. Max 4KB serialized. Defaults to {}. DUPLICATES: creating an exact duplicate (same url AND same event types AND same viewIds as an existing subscription, in any status) returns a userError on ["input","url"] naming the existing subscription id instead of creating a second subscription. Reusing a url and event types for a DIFFERENT set of viewIds is allowed — those deliver different views. PARTIAL OVERLAP IS NOT BLOCKED: a viewId present in two subscriptions on the same url is delivered once PER subscription, so that url receives the same event twice. Check getWebhookSubscriptions before reusing a url, and prefer editing the existing subscription's viewIds over creating an overlapping one. SIGNING SECRET (store it now — shown ONCE): the response `secret` (whsec_...) is returned in plaintext ONLY here and from rotateWebhookSubscriptionSecret. It is stored encrypted and NEVER returned again. Relay it to the user immediately and instruct them to save it; there is no way to recover it later (they would have to rotate). TIER LIMITS: the workspace's plan caps the number of subscriptions and views-per-subscription (a violation returns a userError whose message states the limit and current usage). OUTPUT: { webhookSubscription: { id, name, url, viewIds, eventTypes, metadata, status } | null, secret: string | null, userErrors: [{ field, message }] }. `webhookSubscription` and `secret` are both null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["input","url"], ["input","viewIds"]). VERIFY AFTER WRITE: confirm the subscription via getWebhookSubscriptions (the WebhookSubscription type never re-exposes the secret). WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
createWebhookSubscription
PURPOSE: Delete a Collection (a saved tag set) from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the Collection itself — its tag set and every tag association, so the tag is stripped from every item it was applied to and can no longer be used as a filter. WHAT SURVIVES: the ITEMS ARE NEVER DELETED — they simply lose this one tag; every other tag, field, and piece of data on them is untouched. INPUT: - id (required string): the Collection's id, as returned by getCollections / createCollection for this workspace. Resolve ids with getCollections first — NEVER guess a Collection id. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first fetch the Collection with getCollection(id:) and CONFIRM WITH THE USER, echoing the Collection's name back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedCollectionId: null` — deleting an already-deleted Collection is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). AVAILABILITY: Collection management is available only in workspaces whose Collections use the standard storage. In a workspace that stores Collections in an older format, the ids getCollections returns are not accepted here and the call returns a userError without changing anything. OUTPUT: { deletedCollectionId: string | null, userErrors: [{ field, message }] }. `deletedCollectionId` echoes the deleted id on success, or is null when the Collection was not found. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal with getCollection(id:) — it returns null once the Collection is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteCollection
PURPOSE: Delete a saved content (media deck) view from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the saved view itself — its stored filters, sort, and settings. It disappears from the workspace's saved views. WHAT SURVIVES: the underlying CONTENT IS NEVER DELETED — the items the view listed remain in the workspace untouched; only this saved filter configuration is removed. INPUT: - id (required string): the view's canonical FilterPreset UUID (as returned by getContentViews / createContentView). Resolve ids with getContentViews first — NEVER guess. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first fetch the view with getContentView(id:) and CONFIRM WITH THE USER, echoing the view's name back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedContentViewId: null` — deleting an already-deleted view is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { deletedContentViewId: string | null, userErrors: [{ field, message }] }. `deletedContentViewId` echoes the deleted id on success, or is null when the view was not found. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal with getContentView(id:) — it returns null once the view is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteContentView
PURPOSE: Delete a saved Creator View from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the saved view itself — its stored filters, sort, and settings. It disappears from the workspace's saved views. WHAT SURVIVES: the underlying CREATORS ARE NEVER DELETED — the creators the view listed remain in the workspace untouched; only this saved filter configuration is removed. INPUT: - id (required string): the view's canonical FilterPreset UUID (as returned by getCreatorViews / createCreatorView). Resolve ids with getCreatorViews first — NEVER guess. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first fetch the view with getCreatorView(id:) and CONFIRM WITH THE USER, echoing the view's name back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedCreatorViewId: null` — deleting an already-deleted view is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { deletedCreatorViewId: string | null, userErrors: [{ field, message }] }. `deletedCreatorViewId` echoes the deleted id on success, or is null when the view was not found. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal with getCreatorView(id:) — it returns null once the view is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteCreatorView
PURPOSE: Delete a saved Social Profile view from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the saved view itself — its stored filters, sort, and settings. It disappears from the workspace's saved views. WHAT SURVIVES: the underlying SOCIAL PROFILES ARE NEVER DELETED — the profiles the view listed remain in the workspace untouched; only this saved filter configuration is removed. INPUT: - id (required string): the view's canonical FilterPreset UUID (as returned by getSocialProfileViews / createSocialProfileView). Resolve ids with getSocialProfileViews first — NEVER guess. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first fetch the view with getSocialProfileView(id:) and CONFIRM WITH THE USER, echoing the view's name back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedSocialProfileViewId: null` — deleting an already-deleted view is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { deletedSocialProfileViewId: string | null, userErrors: [{ field, message }] }. `deletedSocialProfileViewId` echoes the deleted id on success, or is null when the view was not found. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal with getSocialProfileView(id:) — it returns null once the view is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteSocialProfileView
PURPOSE: Delete a view group from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the view group itself — the sidebar bucket and its membership rows. It disappears from the workspace's groups. WHAT SURVIVES: the MEMBER VIEWS ARE NEVER DELETED — every Content View and Social Profile View that was in the group simply becomes ungrouped. Their FilterPreset UUIDs are returned as `movedViewIds` so you can re-file them into another group if you want. INPUT: - id (required string): the view group's UUID (as returned by getViewGroups / createViewGroup). Resolve ids with getViewGroups first — NEVER guess a group id. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first fetch the group with getViewGroup(id:) and CONFIRM WITH THE USER, echoing the group's name back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedViewGroupId: null` and `movedViewIds: []` — deleting an already-deleted group is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { deletedViewGroupId: string | null, movedViewIds: [string], userErrors: [{ field, message }] }. `deletedViewGroupId` echoes the deleted id on success, or is null when the group was not found. `movedViewIds` lists the FilterPreset UUIDs of the views that were ungrouped (empty on not-found or when the group had no members) — invalidate any cached ContentView / SocialProfileView rows in this list. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal with getViewGroup(id:) — it returns null once the group is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteViewGroup
PURPOSE: Delete a webhook subscription from the current workspace. This is a HARD, IRREVERSIBLE delete. WHAT IS DESTROYED: the subscription itself AND all of its delivery records (the deliveries cascade). The endpoint stops receiving any further events immediately. WHAT SURVIVES: the content views the subscription pointed at are NEVER deleted — only this subscription (and its delivery history) is removed. INPUT: - id (required string): the subscription's UUID (as returned by getWebhookSubscriptions). Resolve ids with getWebhookSubscriptions first — NEVER guess. CONFIRM BEFORE CALLING: because this is IRREVERSIBLE, first look up the subscription and CONFIRM WITH THE USER, echoing the subscription's name/url back to them, before you call this tool. FAILURES: an unknown id, an already-deleted id, or an id owned by another workspace returns a not_found userError (field ["id"]) with `deletedWebhookSubscriptionId: null` — deleting an already-deleted subscription is a safe no-op (idempotent). Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { deletedWebhookSubscriptionId: string | null, userErrors: [{ field, message }] }. `deletedWebhookSubscriptionId` echoes the deleted id on success, or is null when the subscription was not found. `userErrors[].field` is an array of path segments (e.g. ["id"]). VERIFY AFTER DELETE: confirm removal by calling getWebhookSubscriptions — the id is gone. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
deleteWebhookSubscription
PURPOSE: 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. INPUT: - id (required string): the failure-disabled subscription's UUID. Resolve it via getWebhookSubscriptions first (look for status DISABLED_BY_FAILURES) — never guess. - replayFailedSince24h (optional boolean, default true): when true, replays each failed delivery from the last 24h once. Pass false to re-enable WITHOUT replaying the backlog. ONLY DISABLED_BY_FAILURES SUBSCRIPTIONS ARE ENABLEABLE: a subscription in any other status (active / disabled_by_user) returns a not_disabled_by_failures userError (field ["id"]) and changes nothing. An already-enabled subscription is no longer DISABLED_BY_FAILURES, so re-calling is a safe no-op not_disabled_by_failures (this is what makes the tool idempotent-safe). To resume a user-paused (DISABLED_BY_USER) subscription, use updateWebhookSubscription with status ACTIVE. FAILURES: an unknown id, or a subscription owned by another workspace, returns a not_found userError (field ["id"]) — same observable on purpose (no cross-workspace existence leak). OUTPUT: { webhookSubscription: { id, name, url, viewIds, eventTypes, metadata, status, consecutiveFailures, lastSuccessAt, disabledAt } | null, userErrors: [{ field, message }] }. On success `webhookSubscription.status` is `ACTIVE` and `consecutiveFailures` is 0. `webhookSubscription` is null on a not_found / not_disabled_by_failures failure. `userErrors[].field` is an array of path segments. VERIFY AFTER WRITE: confirm by calling getWebhookSubscriptions (status back to ACTIVE); if you replayed, poll getWebhookDeliveries (filter by subscriptionId) for the catch-up attempts settling. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
enableWebhookSubscription
PURPOSE: Look up a single Collection (saved tag set) by id in the current workspace. INPUT: id (required string — a Collection id as returned by getCollections / createCollection for this workspace). Ids differ between workspaces depending on how each one stores Collections, so always resolve the id through getCollections rather than constructing or reusing one. OUTPUT: { collection: { id, name, itemCount } | null }. Returns { collection: null } (NOT an error) when no Collection with that id exists in the current workspace (unknown id, or id owned by another workspace — same observable, on purpose). `itemCount` is the number of items tagged with the Collection in this workspace. In workspaces whose Collections are preset-backed you can pair the returned `id` with searchItems(presetId:) to fetch the items tagged with the Collection; that pairing does not apply where the workspace uses the older storage. Creating/renaming/deleting a Collection (createCollection / updateCollection / deleteCollection) is available only in workspaces whose Collections use the standard storage — where the workspace uses the older storage those calls return a userError and change nothing. Tagging items with addItemToCollections / removeItemFromCollections works in both. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCollection
PURPOSE: Look up a single Competitor Insights brand by id in the current workspace, with aggregate metrics over a period/date window. Also resolves the workspace's own brand by its stable id (isOwnBrand: true). INPUT: id (required string, the brand's Archive id), optional period ("WEEK"|"MONTH", default "MONTH") and date (ISO date, defaults to today). OUTPUT: { id, name, isOwnBrand, earnedMediaValue, engagementsTotal, impressions, reach, postsCount, influencersCount, ownMetricsStatus } on success. For the own entry, ownMetricsStatus is AVAILABLE (null metrics then mean no data in the window) or UNAVAILABLE (retry later); it is null for competitors. Returns an isError tool result with the text "Competitor brand not found" when no brand with that id is tracked in the current workspace (unknown id, or known id tracked only by another workspace — same observable, on purpose). WARNINGS: If the metrics store is temporarily unavailable, brand identity data is still returned with null metrics and a `warnings` array containing the message below. An own-brand result has ownMetricsStatus "UNAVAILABLE". Back off for minutes before retrying; do not immediately repeat the call. "Competitor metrics are temporarily unavailable. Retry later." RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCompetitorBrand
PURPOSE: Look up a single saved content (media deck) view by id in the current workspace. INPUT: id (required string, the content view's Archive id / FilterPreset UUID). OUTPUT: { contentView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null }. Returns { contentView: null } (NOT an error) when no media_deck view with that id exists in the current workspace (unknown id, or id owned by another workspace — same observable, on purpose). Pair the returned `id` with searchItems(presetId:) to fetch the items belonging to the view. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getContentView
PURPOSE: Look up a single creator (influencer) by id in the current workspace. INPUT: id (required string, the creator's Archive id). OUTPUT: { id, customAttributes } on success. Returns an isError tool result with the text "Creator not found" when no creator with that id exists in the current workspace (unknown id, or known id owned by another workspace — same observable, on purpose). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCreator
PURPOSE: Look up a single saved Creator View by id in the current workspace. INPUT: id (required string — the Creator View's Archive id / FilterPreset UUID, as returned by getCreatorViews). OUTPUT: { creatorView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null }. Returns { creatorView: null } (NOT an error) when no creator view with that id exists in the current workspace (unknown id, or id owned by another workspace — same observable, on purpose). Pair the returned `id` with searchCreators(presetId:) to fetch the view's creators. To create/update/delete use createCreatorView / updateCreatorView / deleteCreatorView. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCreatorView
PURPOSE: List custom attribute schemas for the current workspace, used both to INTERPRET values in the `customAttributes` JSON field on items/creators AND to BUILD `customAttributeConditions` filters for searchItems / searchCreators. INPUT: entity (required: "ITEM" or "CREATOR"). ITEM → schemas attached to items (`shop_item`); CREATOR → schemas attached to creators. OUTPUT: { items: [{ key, name, type, aiGenerated, options: [{ id, name }] }] } - `key` matches a top-level field inside the entity's customAttributes hash (and is the `field` in a filter condition). - `type` is the upper-case attribute type (TEXT, SINGLE_SELECT_V2, MULTIPLE_SELECT_V2, etc.) — pass it as `type` in a filter condition. - `aiGenerated` is true when the attribute's value is produced by an AI Filter; searchItems returns the model's explanation for those keys in each item's `aiFilterReasons`. - `options` is `[]` for non-select schemas; for select-type schemas each option's `id` is what appears as the value (or array element) in customAttributes and is the `value` you pass when filtering on that field. CUSTOM ATTRIBUTE FILTERS (customAttributeConditions): Filter by the workspace's user-defined custom fields. An array of { field, operator, type, value } objects; multiple entries are AND-ed together. Step 1 - discover fields: call getCustomAttributeSchemas(entity: ITEM | CREATOR). Each schema returns `key` (use as `field`), `type` (use as `type`), and `options: [{ id, name }]` for select fields (use an option `id` as `value`). Step 2 - build each condition: - field : the schema `key` (e.g. "sentiment", "links"). - type : the schema `type`, UPPERCASE - one of TEXT, EMAIL, PHONE, URL, NUMBER, BOOLEAN, DATE, DATETIME, SINGLE_SELECT_V2, SINGLE_SELECT_V3, MULTIPLE_SELECT, MULTIPLE_SELECT_V2, TEXT_LIST, NUMBER_LIST, DATE_LIST, DATETIME_LIST, BOOLEAN_LIST. - operator : UPPERCASE; the valid set depends on the field's type group (see below). - value : depends on `type`: * TEXT / EMAIL / PHONE / URL -> a string. * SINGLE_SELECT_V2 / SINGLE_SELECT_V3 -> the chosen option `id` (UUID). * NUMBER -> a number; BETWEEN takes { from, to }. * DATE / DATETIME -> ISO-8601 string; BETWEEN takes { from, to }. * BOOLEAN -> true / false. * MULTIPLE_SELECT / MULTIPLE_SELECT_V2 -> array of option `id`s. * TEXT_LIST / NUMBER_LIST / DATE_LIST / ... -> array of values. * IS_EMPTY / IS_NOT_EMPTY -> value is ignored; pass null. Operators by type group (passing an operator outside its group is rejected with a validation error naming the field; a SHIPPING_ADDRESS-typed condition is always rejected the same way — that is the SHIPPING_ADDRESS *type*, distinct from the `shipping_address` *field* whose stored-only matching semantics are noted below): - Text & single-select (TEXT, EMAIL, PHONE, URL, SINGLE_SELECT_V2, SINGLE_SELECT_V3): IS, IS_NOT, CONTAINS, DOES_NOT_CONTAIN, STARTS_WITH, ENDS_WITH, IS_EMPTY, IS_NOT_EMPTY. (IS / IS_NOT are exact match - for a select field, value is the option id.) - Number (NUMBER): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY. - Date (DATE, DATETIME): EQUAL, NOT_EQUAL, MORE_THAN, MORE_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, BETWEEN, IS_EMPTY, IS_NOT_EMPTY, IS_RELATIVE_TO_TODAY. IS_RELATIVE_TO_TODAY value: { relation: "past"|"this"|"next", period: "day"|"week"|"month"|"year" } (offset optional); an empty or unrecognized value makes the condition a no-op. - Boolean (BOOLEAN): IS (value true / false). - Multi-value (MULTIPLE_SELECT, MULTIPLE_SELECT_V2, and every *_LIST type): CONTAINS (matches ANY of the values, OR), CONTAINS_ALL (must contain ALL, AND), DOES_NOT_CONTAIN, IS_EMPTY, IS_NOT_EMPTY. NOTE: two schemas are stored but NOT indexed for filtering — the AI free-text summary (post_summary) and shipping_address. For post_summary/shipping_address: positive operators (IS, CONTAINS, STARTS_WITH, etc.) match NOTHING; negated operators (IS_NOT, DOES_NOT_CONTAIN, IS_EMPTY) match EVERY record — do not filter on these fields; read their values from each item's `customAttributes` instead. NOTE: silently-ignored conditions — on items, conditions on `labels` and `post_date` are ignored (no error, no filtering effect); on creators, conditions on `full_name` may be ignored (feature-flag-gated). Examples: [{ "field": "sentiment", "operator": "IS", "type": "SINGLE_SELECT_V2", "value": "<option-uuid>" }] [{ "field": "links", "operator": "CONTAINS", "type": "TEXT_LIST", "value": ["https://example.com/promo"] }] [{ "field": "lead_score", "operator": "BETWEEN", "type": "NUMBER", "value": { "from": 10, "to": 100 } }] [{ "field": "notes", "operator": "IS_NOT_EMPTY", "type": "TEXT", "value": null }] RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCustomAttributeSchemas
PURPOSE: Paginated history of engagement metric snapshots for a single item, newest first. INPUT: itemId (required string). Optional cursor, limit (default 20, max 100), and filter { capturedAt: { from, to } } (ISO-8601 date-time bounds, UTC only — offsets other than Z/+00:00/-00:00 are rejected; both bounds are optional). An unknown or foreign itemId returns an isError result with the coded text "Item not found". OUTPUT: { items: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each item node is { at, likes, comments, views, shares, impressions, earnedMediaValue, followers, linearViralityScore }. Metric values in nodes may be null when data has not been captured. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getEngagementHistory
PURPOSE: Retrieve media contents (images / videos) for shop items OR for tracked Competitor Insights items in the current workspace. INPUT: exactly one of itemIds (Item / shop-item UUIDs) OR competitorBrandItemIds (CompetitorBrandItem UUIDs from getCompetitorBrandItems). Each is an array of 1..100 string IDs. Passing both, or neither, returns an isError result. OUTPUT: { items: [...] } — one node per media-content row. Each node carries id, mediaItemId, type, fileUrl, thumbnailUrl, width, height, deleted; videos additionally carry videoDuration. NOTE: fileUrl and thumbnailUrl are raw CDN URLs (unsigned); YouTube nodes always have fileUrl: null and thumbnailUrl may be null. Unknown, cross-workspace, or non-entitled competitor item IDs yield zero rows (not an error). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getMediaContents
PURPOSE: Look up a single async operation by id in the current workspace — poll its status and progress (e.g. after refetchEngagementBulk). INPUT: id (required string, the operation's Archive id). OUTPUT: { id, status, operationType, total, processed, succeededCount, failedCount, pendingCount, succeededItemIds, failedItemIds, pendingItemIds, itemIdsTruncated, createdAt, completedAt } on success (completedAt is null until the operation finishes). The three counts are always exact. Each item-id list holds at most 1000 ids: when itemIdsTruncated is true some ids were omitted — use the counts for exact sizes and page through getOperationRecords for the remaining ids; do not report a partial list as the whole outcome. Counts and id lists are separate reads: while status is QUEUED or PROCESSING they are a moving snapshot and may briefly disagree as records finish; on a terminal status records no longer change, so the response is exact and self-consistent. status values: QUEUED (not yet started), PROCESSING (in progress), COMPLETED (all items succeeded — terminal), PARTIAL (finished with some failures — terminal), FAILED (all items failed — terminal). Returns an isError tool result with the text "Operation not found" when no operation with that id exists in the current workspace (unknown id, or known id owned by another workspace — same observable, on purpose). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getOperation
PURPOSE: Page through the per-item records of one operation, newest first — the follow-up to getOperation when itemIdsTruncated is true, and the way to enumerate an operation's items with their per-item status. INPUT: operationId (required string), optional status filter (SUCCEEDED | FAILED | PENDING — PENDING covers not-started, in-progress and retrying records), optional cursor, limit (default 100, max 1000). OUTPUT: { records: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each record is { itemId, status }; itemId is a documented input to getMediaContents / getTranscriptions. totalCount is the exact number of records matching the filter. Returns an isError tool result with the text "Operation not found" when no operation with that id exists in the current workspace, and "Invalid cursor" for a cursor this connection did not issue. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getOperationRecords
PURPOSE: Look up ONE social profile (influencer) in the current workspace. For MANY profiles do NOT loop this per id — call getSocialProfiles once to list/filter a whole page of profiles in a single call (the batch path). INPUT: provide EXACTLY ONE of: - id (the social profile's Archive id), OR - accountName + provider (provider is one of instagram, tiktok, youtube). Optional fallback (default false): when true and the profile is not archived locally, fetch it live from the social platform (slower). Passing both id and accountName/provider, or accountName without provider, or neither, returns an isError result. OUTPUT: { socialProfile: { id, provider, originalUrl, accountName, avatar, private, verified, followers, following, fullName, email, phoneNumbers, proAccount, creator { id } } } on success, or { socialProfile: null } when no profile matches in the current workspace (workspace-scoped lookup; profiles archived only in other workspaces are treated as misses and return null, not an error). With fallback: true a miss may instead return isError: true with one of: - "Upstream social-platform service is temporarily unavailable. Please retry." (UPSTREAM_UNAVAILABLE code in extensions — safe to retry); - a PROVIDER_ERROR message from the platform (e.g. rate-limited); - "User not found on <provider>." (the account does not exist on the platform). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getSocialProfile
PURPOSE: Look up a single saved Social Profile view by id in the current workspace. INPUT: id (required string — the Social Profile view's Archive id / FilterPreset UUID, as returned by getSocialProfileViews). OUTPUT: { socialProfileView: { id, name, filters, customAttributeConditions, sort, showReportingStats, group } | null }. Returns { socialProfileView: null } (NOT an error) when no social-profiles view with that id exists in the current workspace (unknown id, or id owned by another workspace — same observable, on purpose). Pair the returned `id` with getSocialProfiles(presetId:) to fetch the view's social profiles. To create/update/delete use createSocialProfileView / updateSocialProfileView / deleteSocialProfileView. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getSocialProfileView
PURPOSE: Fetch transcriptions for the media contents of one or more shop items in the current workspace. INPUT: itemIds (required) — an array of 1..1000 Item / shop-item UUIDs. OUTPUT: { items: [...] } — one node per media content that has a non-empty transcript across the requested items. Each node carries mediaContentId and transcript. Items with no (or only empty) transcriptions yield no rows. NOTE: v0 exposes only the itemIds (Elasticsearch) path; the deferred mediaContentIds (DB) path is not available. NOTE: rate-limit cost scales with the number of itemIds but is capped at 100 — a request with more than 100 ids (up to the 1000 max) is charged as 100. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getTranscriptions
PURPOSE: Look up a single user-created view group by id in the current workspace, including its member views. INPUT: id (required string — the ViewGroup UUID as returned by getViewGroups / createViewGroup). OUTPUT: { viewGroup: { id, name, contentViews: [{ id, name }], socialProfileViews: [{ id, name }], creatorViews: [{ id, name }] } | null }. Member views carry id and name only — fetch a member's configuration (filters, sort, …) with getContentView / getSocialProfileView / getCreatorView. `contentViews`, `socialProfileViews`, and `creatorViews` enumerate the group's member views in their within-group position order (ties break on id for a stable order). CAMPAIGN Creator Views are never group members (they are campaign-scoped, not workspace-scoped) — workspace Creator Views ARE groupable and appear in `creatorViews`. Returns { viewGroup: null } (NOT an error) when no group with that id exists in the current workspace (unknown id, or id owned by another workspace — same observable, on purpose). To create/rename/delete a group use createViewGroup / updateViewGroup / deleteViewGroup; to move a view into/out of a group use moveContentViewToGroup / moveSocialProfileViewToGroup / moveCreatorViewToGroup. reorderViewsInGroup rewrites member positions — read the full member list from this tool first, since a reorder that omits members leaves their positions unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getViewGroup
PURPOSE: Get a workspace — its tracked hashtags/mentions/keywords and connected social integrations — in one call. The workspace is selected by the optional `workspaceId` argument (falling back to the `WORKSPACE-ID` header, or your sole workspace when you can access exactly one). Call getWorkspaces first to discover the ids you can access. INPUT: optional workspaceId — the workspace UUID (from getWorkspaces) or the numeric workspace id shown in the Archive app. OUTPUT: { workspace: { id, name, hashtags: [...], mentions: [...], keywords: [...], integrations: [...] } }. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getWorkspace
PURPOSE: List campaigns for the current workspace, newest first. INPUT: optional cursor, limit (default 20, max 100). No filter, no sorting (results are always newest first). OUTPUT: { campaigns: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each campaign node is { id, name, createdAt }. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCampaigns
PURPOSE: List Collections (saved tag sets) for the current workspace, in the workspace's saved display order. INPUT: none (beyond the workspace selector). OUTPUT: { items: [{ id, name, itemCount }] } `id` is the id this workspace accepts for addItemToCollections / removeItemFromCollections and for getCollection(id:). ALWAYS take Collection ids from this tool — never construct them, cache them, or reuse one across workspaces. Some workspaces store Collections in an older format and their ids look different; the ids listed here are always the ones that workspace accepts. `itemCount` is the number of items in this workspace tagged with the Collection. In workspaces whose Collections are preset-backed you can pair a Collection's `id` with searchItems(presetId:) to fetch the items tagged with that Collection; that pairing does not apply where the workspace uses the older storage. Creating/renaming/deleting a Collection (createCollection / updateCollection / deleteCollection) is available only in workspaces whose Collections use the standard storage — where the workspace uses the older storage those calls return a userError and change nothing. Tagging items with addItemToCollections / removeItemFromCollections works in both. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCollections
PURPOSE: Paginate posts attributed to a single tracked Competitor Insights brand within a required `takenAt` time window. INPUT: brandId (required string, the brand's Archive id — not an ISO-8601 date-time); takenAtFrom and takenAtTo (required ISO-8601 date-time, UTC only — offsets other than Z/+00:00/-00:00 are rejected); optional sorting ({ sortKey: TAKEN_AT|EARNED_MEDIA_VALUE, sortOrder: ASC|DESC }, default TAKEN_AT DESC), limit (default 20, max 100), cursor, responseFormat (concise | detailed, default detailed — see OUTPUT). An unknown or foreign brandId returns an empty connection (not an error). EARNED_MEDIA_VALUE ranks posts as one ordering across the whole window. OUTPUT: { items: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. responseFormat "detailed" (default) returns the full per-node shape; "concise" trims each node to id, takenAt, provider, type, originalUrl, caption, socialProfile { id accountName }, and currentEngagement — for listing / filtering / ranking. NOTE: a month's posts become available on day 9 of the following month — the current month is never queryable. The cursor is positional — pass pageInfo.endCursor back with the same brandId, takenAtFrom/takenAtTo, and sorting; responseFormat may change freely between pages. A malformed cursor (including one minted before the ClickHouse cutover) is rejected with "Invalid cursor", and pagination depth is capped at 10,000 posts per query — narrow the takenAt window to go deeper. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCompetitorBrandItems
PURPOSE: List Competitor Insights brands tracked for the current workspace, newest first, with aggregate metrics over a period/date window, so self-vs-competitor Share of Voice is computable in one call. INPUT: optional cursor, limit (default 20, max 100), period ("WEEK"|"MONTH", default "MONTH"), date (ISO date, defaults to today). No filter, no sorting (results are always newest first). OUTPUT: { brands: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each brand node is { id, name, isOwnBrand, earnedMediaValue, engagementsTotal, impressions, reach, postsCount, influencersCount, ownMetricsStatus }. The workspace's own brand is returned as one additional pinned node on the first page (isOwnBrand: true, name "You"), in addition to up to `limit` competitor brands; totalCount counts competitor brands only. For the own entry, ownMetricsStatus is AVAILABLE (null metrics then mean no data in the window) or UNAVAILABLE (retry later); it is null for competitors. WARNINGS: If the metrics store is temporarily unavailable, the roster is still returned with null metrics, ownMetricsStatus "UNAVAILABLE", and a `warnings` array containing the message below. Back off for minutes before retrying; do not immediately repeat the call. "Competitor metrics are temporarily unavailable. Retry later." RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCompetitorBrands
PURPOSE: List saved content (media deck) views for the current workspace, ordered most-recently-updated first. INPUT: optional groupId (filters to one group; omitted/null returns all media_deck views). An unknown or foreign groupId returns an empty list (not an error). OUTPUT: { items: [{ id, name, filters, customAttributeConditions, sort, showReportingStats, group }] } Pair an item's `id` with searchItems(presetId:) to fetch the items belonging to a view. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getContentViews
PURPOSE: List saved Creator Views for the current workspace, ordered most-recently-updated first. INPUT: optional groupId (filters to one group; omitted/null returns all creator views). An unknown or foreign groupId returns an empty list (not an error). OUTPUT: { items: [{ id, name, filters, customAttributeConditions, sort, showReportingStats, group }] } Pair an item's `id` with searchCreators(presetId:) to fetch the view's creators. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getCreatorViews
PURPOSE: DEPRECATED — prefer the typed view tools getContentViews (accessor MEDIA_DECK) and getCollections (accessor COLLECTIONS); their ids work the same way as a presetId input. This tool still lists saved filter presets for the current workspace (no removal window announced), but new integrations should not adopt it. Use the returned id as `presetId:` when calling items(...) to scope a search to the preset's saved filter set. INPUT: none (no arguments). OUTPUT: { items: [{ id, name, accessor }, ...] }. `accessor` is one of "MEDIA_DECK" or "COLLECTIONS" (the resolver only exposes those two; presets with other accessors or `visible: false` are filtered out). Ordered by the workspace's default group position, then by creation time. Empty workspaces return `{ items: [] }`. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getFilterPresets
PURPOSE: List operations for the current workspace, newest first. Only `refetch_engagement` operation type is listed here; an operation id visible via getOperation may be absent from this list if it is of another internal type. INPUT: optional cursor, limit (default 20, max 100). No filter, no sorting (results are always newest first). OUTPUT: { operations: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each operation node is { id, status, operationType, total, createdAt }. status values: QUEUED (not yet started), PROCESSING (in progress), COMPLETED (all items succeeded — terminal), PARTIAL (finished with some failures — terminal), FAILED (all items failed — terminal). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getOperations
PURPOSE: List saved Social Profile views for the current workspace, ordered most-recently-updated first. INPUT: optional groupId (filters to one group; omitted/null returns all social-profile views). An unknown or foreign groupId returns an empty list (not an error). OUTPUT: { items: [{ id, name, filters, customAttributeConditions, sort, showReportingStats, group }] } Pair an item's `id` with getSocialProfiles(presetId:) to fetch the view's social profiles. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getSocialProfileViews
PURPOSE: List social profiles (Instagram / TikTok / YouTube accounts) tracked for the current workspace, profiles with the most recent content first. This is the batch / multi-profile path — reach for it instead of calling getSocialProfile once per id when you need more than one profile. INPUT (all optional): - filter (object): a SocialProfileFilterInput. Fields: * platform: one of INSTAGRAM, TIKTOK, YOUTUBE, INTERNAL. - presetId (string): a saved Social Profile View id (from getSocialProfileViews / getSocialProfileView). When set, the view's stored customAttributeConditions and sort drive the results and the inline filter argument is IGNORED (the view's `filters` blob is never applied on read). A non-Social-Profile-View id returns a coded error with extensions.code = "WRONG_VIEW_TYPE"; an unknown or foreign id returns extensions.code = "NOT_FOUND" (cross-workspace ids collapse to NOT_FOUND — no enumeration leak). - cursor (string), limit (integer, default 20, max 100). Cursor note: pass pageInfo.endCursor only — per-node edge cursors are not valid here. - responseFormat (string): "concise" or "detailed" (default) — see OUTPUT. No sorting argument — results are most-recent-content first (a preset's stored sort overrides this). OUTPUT: { socialProfiles: [{ id, provider, originalUrl, accountName, avatar, private, verified, followers, following, fullName, email, phoneNumbers, proAccount, creator { id } }], pageInfo: { hasNextPage, endCursor }, totalCount } responseFormat "detailed" (default) returns the node shape above; "concise" returns { id, accountName, provider, followers, verified } per node — for listing / filtering / ranking. Use "detailed" when contact fields (email, phoneNumbers, fullName) or the full profile shape are needed. NOTE: no inline custom-attribute filter exists on this query — to narrow by custom attributes, create/point at a Social Profile View with customAttributeConditions (createSocialProfileView) and pass its id as presetId. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getSocialProfiles
PURPOSE: List user-created view groups for the current workspace, ordered oldest-first. INPUT: none (beyond the workspace selector). OUTPUT: { items: [{ id, name }] } This list view returns group metadata only. To see a group's member views (Content Views + Social Profile Views + Creator Views, in position order) call getViewGroup(id:). To create/rename/delete a group use createViewGroup / updateViewGroup / deleteViewGroup; to move a view into/out of a group use moveContentViewToGroup / moveSocialProfileViewToGroup / moveCreatorViewToGroup; to reorder members use reorderViewsInGroup (fetch the full member list via getViewGroup first). RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getViewGroups
PURPOSE: Paginate webhook deliveries (the dead-letter / observability surface) for the current workspace, newest first. THE source for redeliverWebhookDelivery's required `deliveryId` — filter by status "FAILED" and pick the delivery's `id`; never guess ids. INPUT: - subscriptionId (optional string): only deliveries for this subscription (resolve via getWebhookSubscriptions). A foreign/unknown id yields an empty page. - status (optional string): PENDING | DELIVERING | SUCCEEDED | FAILED | DROPPED. - limit (optional integer): page size, default 20, max 100. - cursor (optional string): pageInfo.endCursor from the previous page. OUTPUT: { deliveries: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each delivery node is { id, subscriptionId, eventId, status, attemptCount, lastAttemptAt, nextAttemptAt, lastResponseStatus, lastError, responseTimeMs, createdAt, updatedAt }. status values: PENDING (queued/scheduled), DELIVERING (in flight), SUCCEEDED (2xx — terminal), FAILED (retried on the 8-attempt backoff ladder until exhausted; a terminal FAILED — nextAttemptAt null — is redeliverable via redeliverWebhookDelivery), DROPPED (backpressure/rate-limit — terminal, never retried; recover the business event via getWebhookEvents). NOTE: after sendWebhookTestEvent / redeliverWebhookDelivery / enableWebhookSubscription, poll here (filter by subscriptionId) for the attempts settling. RETENTION: delivery history is pruned after 30 days — redeliver within that window. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getWebhookDeliveries
PURPOSE: Paginate webhook events (the outbox — the Stripe /v1/events pattern) for the current workspace, newest first. The reconciliation surface: query it to recover business events whose deliveries were DROPPED or missed; an event's `id` doubles as its idempotency key. INPUT: - eventTypes (optional array of strings): only events whose type is in this list. content_view.item_added is the only subscribable type in v1; system types subscription.disabled and subscription.deliveries_dropped also appear in the outbox. - limit (optional integer): page size, default 20, max 100. - cursor (optional string): pageInfo.endCursor from the previous page. OUTPUT: { events: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each event node is { id, eventType, eventVersion, payload, createdAt } — `payload` is the exact JSON delivered to subscribers. NOTE: cross-reference a delivery's `eventId` (from getWebhookDeliveries) to its outbox row here. RETENTION: outbox history is pruned after 30 days — reconcile within that window. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getWebhookEvents
PURPOSE: List outbound-webhook subscriptions for the current workspace, newest first. The id-resolution source for updateWebhookSubscription / deleteWebhookSubscription / rotateWebhookSubscriptionSecret / sendWebhookTestEvent / enableWebhookSubscription — resolve subscription ids here, never guess. INPUT: - limit (optional integer): page size, default 20, max 100. - after (optional string): nextCursor from the previous page. All subscriptions fit one page today (workspace cap), so paging is only needed once caps rise. OUTPUT: { items: [{ id, name, url, eventTypes, viewIds, metadata, status, consecutiveFailures, lastSuccessAt, disabledAt, updatedAt }], nextCursor, hasNextPage } status values: ACTIVE (delivering), DISABLED_BY_USER (manual pause — re-enable via updateWebhookSubscription with status ACTIVE), DISABLED_BY_FAILURES (system auto-disable — re-enable via enableWebhookSubscription). `consecutiveFailures` is the health signal; `updatedAt` is the rotation-confirmation signal referenced by rotateWebhookSubscriptionSecret. When hasNextPage is true, pass `after: nextCursor` to fetch the next page. NOTE: the signing secret is NEVER returned here (or anywhere after issuance) — it is returned in plaintext exactly once, by createWebhookSubscription / rotateWebhookSubscriptionSecret. To inspect delivery health use getWebhookDeliveries. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getWebhookSubscriptions
PURPOSE: List all workspaces the authenticated caller can access, newest first. INPUT: optional cursor, limit (default 20, max 100). No filter, no sorting (results are always newest first). OUTPUT: { workspaces: [...], pageInfo: { hasNextPage, endCursor }, totalCount }. Each workspace node is { id, name, hashtags, mentions, keywords, integrations }. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
getWorkspaces
PURPOSE: 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. ONE GROUP PER VIEW: a Collection belongs to at most one group at a time. Moving it into a new group IMPLICITLY leaves whatever group it was in before — you do not need to remove it first. INPUT: - collectionId (required string): the Collection's id (from getCollections / getCollection) for this workspace. Resolve ids there first — NEVER guess. - groupId (optional string OR null): the target view group's UUID (from getViewGroups / createViewGroup). OMIT it or pass an explicit null to REMOVE the Collection from any group it is currently in (ungroup). Omitted and explicit null are equivalent here. FAILURES: a collectionId that is unknown, owned by another workspace, or not a Collection (e.g. a Content View or a Creator View) returns a not_found userError on ["collectionId"]. A groupId owned by another workspace returns a not_found userError on ["groupId"]. Same observable for unknown vs cross-workspace, on purpose (no existence leak). AVAILABILITY: Collection management is available only in workspaces whose Collections use the standard storage. In a workspace that stores Collections in an older format, the ids getCollections returns are not accepted here and the call returns a userError without changing anything. OUTPUT: { collection: { id, name, group } | null, userErrors: [{ field, message }] }. On success `collection.group` reflects the new group (null after an ungroup). `collection` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["collectionId"], ["groupId"]). VERIFY AFTER WRITE: confirm the move with getCollection(id:) and inspect its `group`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
moveCollectionToGroup
PURPOSE: 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. ONE GROUP PER VIEW: a view belongs to at most one group at a time. Moving it into a new group IMPLICITLY leaves whatever group it was in before — you do not need to remove it first. INPUT: - viewId (required string): the Content View's FilterPreset UUID (from getContentViews / getContentView). Resolve ids there first — NEVER guess. - groupId (optional string OR null): the target view group's UUID (from getViewGroups / createViewGroup). OMIT it or pass an explicit null to REMOVE the view from any group it is currently in (ungroup). Omitted and explicit null are equivalent here. FAILURES: a viewId that is unknown, owned by another workspace, or not a Content View (e.g. a Social Profile View — use moveSocialProfileViewToGroup — or a Creator View — use moveCreatorViewToGroup) returns a not_found userError on ["viewId"]. A groupId owned by another workspace returns a not_found userError on ["groupId"]. Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { contentView: { id, name, group } | null, userErrors: [{ field, message }] }. On success `contentView.group` reflects the new group (null after an ungroup). `contentView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["viewId"], ["groupId"]). VERIFY AFTER WRITE: confirm the move with getContentView(id:) and inspect its `group`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
moveContentViewToGroup
PURPOSE: 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. ONE GROUP PER VIEW: a view belongs to at most one group at a time. Moving it into a new group IMPLICITLY leaves whatever group it was in before — you do not need to remove it first. INPUT: - viewId (required string): the Creator View's FilterPreset UUID (from getCreatorViews / getCreatorView). Resolve ids there first — NEVER guess. - groupId (optional string OR null): the target view group's UUID (from getViewGroups / createViewGroup). OMIT it or pass an explicit null to REMOVE the view from any group it is currently in (ungroup). Omitted and explicit null are equivalent here. FAILURES: a viewId that is unknown, owned by another workspace, or not a Creator View (e.g. a Content View or a Social Profile View) returns a not_found userError on ["viewId"]. A groupId owned by another workspace returns a not_found userError on ["groupId"]. Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { creatorView: { id, name, group } | null, userErrors: [{ field, message }] }. On success `creatorView.group` reflects the new group (null after an ungroup). `creatorView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["viewId"], ["groupId"]). VERIFY AFTER WRITE: confirm the move with getCreatorView(id:) and inspect its `group`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
moveCreatorViewToGroup
PURPOSE: 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. ONE GROUP PER VIEW: a view belongs to at most one group at a time. Moving it into a new group IMPLICITLY leaves whatever group it was in before — you do not need to remove it first. INPUT: - viewId (required string): the Social Profile View's FilterPreset UUID (from getSocialProfileViews / getSocialProfileView). Resolve ids there first — NEVER guess. - groupId (optional string OR null): the target view group's UUID (from getViewGroups / createViewGroup). OMIT it or pass an explicit null to REMOVE the view from any group it is currently in (ungroup). Omitted and explicit null are equivalent here. FAILURES: a viewId that is unknown, owned by another workspace, or not a Social Profile View (e.g. a Content View — use moveContentViewToGroup — or a Creator View — use moveCreatorViewToGroup) returns a not_found userError on ["viewId"]. A groupId owned by another workspace returns a not_found userError on ["groupId"]. Same observable for unknown vs cross-workspace, on purpose (no existence leak). OUTPUT: { socialProfileView: { id, name, group } | null, userErrors: [{ field, message }] }. On success `socialProfileView.group` reflects the new group (null after an ungroup). `socialProfileView` is null on a userErrors failure. `userErrors[].field` is an array of path segments (e.g. ["viewId"], ["groupId"]). VERIFY AFTER WRITE: confirm the move with getSocialProfileView(id:) and inspect its `group`. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
moveSocialProfileViewToGroup
PURPOSE: 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. INPUT: - deliveryId (required string): the failed delivery's UUID (as returned by getWebhookDeliveries, the dead-letter surface). Resolve it with getWebhookDeliveries first — never guess. ONLY `failed` DELIVERIES ARE REPLAYABLE: a delivery in any other status (pending / delivering / succeeded) returns a not_replayable userError (field ["deliveryId"]) and enqueues nothing. An already-replayed delivery is no longer `failed`, so re-calling is a safe no-op not_replayable (this is what makes the tool idempotent-safe). FAILURES: an unknown id, or a delivery owned by another workspace, returns a not_found userError (field ["deliveryId"]) — same observable on purpose (no existence leak). OUTPUT: { webhookDelivery: { id, subscriptionId, eventId, status, attemptCount, lastAttemptAt, nextAttemptAt, lastResponseStatus, lastError, responseTimeMs, createdAt, updatedAt } | null, userErrors: [{ field, message }] }. On success `webhookDelivery.status` is `pending` (re-enqueued). `webhookDelivery` is null on a not_found / not_replayable failure. `userErrors[].field` is an array of path segments. VERIFY AFTER WRITE: poll getWebhookDeliveries (filter by subscriptionId) for the delivery's terminal status once the replay settles. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
redeliverWebhookDelivery
PURPOSE: 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. Credits spent are NOT refundable — this spend is IRREVERSIBLE. INPUT: - itemIds (required array of strings, 1-1000 items): Archive item ids to refresh. - confirm (boolean, default false): required true when itemIds exceeds 50 (see below). COST CONFIRMATION (>50 items): a call with more than 50 itemIds requires confirm: true. Without it the tool DOES NOT execute or spend — it returns a cost quote in userErrors (field ["confirm"]) instead. Relay that cost to the user, and only re-call with confirm: true after they approve. A call with 50 or fewer items proceeds without confirm; confirm: true proceeds at any size (still capped at 1000). ASYNC + ANTI-DOUBLE-SPEND: on execution the refresh runs in the background. Results land under operationId — poll getOperation(id: operationId) until its status is terminal (COMPLETED / PARTIAL / FAILED), THEN call getEngagementHistory for the refreshed values. Do NOT expect fresh metrics in this response. NEVER re-issue the same batch while its operation is still running — a duplicate re-spends credits. A 24h dedup window protects immediate duplicates (items refreshed in the last 24h are skipped, not re-charged), but a fresh batch submitted AFTER the window spends again. SKIPPED ITEMS: skippedItemIds lists items that were NOT charged and NOT queued — items already refreshed within the 24h window. Instagram stories are also excluded (silently, not listed). Do NOT "fix" a skip by retrying it: a skip means the data is already fresh (or the item is a story), so retrying only risks a later re-charge once the window elapses. SOURCING itemIds: get item ids from a fresh searchItems call, then pass them here. Item ids from another workspace are silently ignored (they never count toward the charge). OUTPUT: { operationId: string | null, processedCount: integer | null, skippedItemIds: [string] | null, userErrors: [{ field, message }] }. operationId is null when nothing was processable (all filtered / deduped). userErrors[].field is an array of path segments (e.g. ["confirm"], ["base"]). Common userError cause: insufficient credits. WRITE NOTES (apply to every write tool): - userErrors mean the write DID NOT happen: a non-empty `userErrors` array is a SUCCESSFUL tool result reporting a domain rejection, NOT a crash. The write did not take effect. Read each entry's `field` (an array of path segments) and `message`, correct the input, and either retry or ask the user — do not treat it as a transport error. - Verify after write: after a success, confirm the change with the READ tool this tool's own description names (e.g. a get*/search* tool) before reporting success to the user. Deletes are confirmed when that read returns null. - Retry discipline on a timeout / transport error (you cannot tell if the write landed): tools annotated `idempotentHint: true` MAY be retried directly WITHOUT a verification read — a duplicate apply converges to the same state. For every OTHER (non-idempotent) write, READ FIRST with the verify tool above to check whether it already took effect BEFORE retrying. NEVER blind-retry create* / uploadItemFromUrl / refetchEngagementBulk — a duplicate creates a second entity, a second import, or re-spends credits. - Destructive tools (`destructiveHint: true`) CHANGE OR REMOVE data that already exists, or trigger a real delivery attempt — they are not purely additive. Say what will change before you call one. Being destructive does NOT on its own require a confirmation round-trip; only the IRREVERSIBLE tools named below do. - IRREVERSIBLE tools — every delete*, plus rotateWebhookSubscriptionSecret (the previous signing secret is gone for good) and refetchEngagementBulk (credits spent are not refunded): CONFIRM WITH THE USER first, echoing the target entity's name/id back to them, and state that the effect is IRREVERSIBLE, before you call. No other write tool needs a confirmation round-trip. - Cost: every write charges a flat weighted cost (~50+ points) against the workspace's shared rate-limit bucket; the existing per-workspace throttle semantics (below) apply unchanged. RUNTIME NOTES: - Rate limiting: a weighted per-workspace rate limit is enforced on every call. A throttled call returns isError text: "Rate limit exceeded. Retry after N seconds." - Argument validation: arguments failing schema validation return isError text like "Invalid argument '$.<path>': <reason>". - Unexpected failures: any other error returns isError text "Tool execution failed".
refetchEngagementBulk
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Archive alternatives on ChatGPT?
As of 2026-09-13, Archive competes with Heepsy Influencer Search, Influencer Hero, Influencers Club, Linktree, Upfluence, Wednesday.app in ChatGPT Influencer & Creator Discovery, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.