Outcome
Remove selected memberships while keeping the prospect contacts.
Before you start
- Verify the environment, credential, authorized organization and the published action permission workspace:write.
- Discover an accessible Engage workspace and bind resourceType=workspace and its resourceId separately from action input. Fixture identifiers grant no access.
- Approve the exact mutation and its effects before CLI --confirm or MCP confirm:true. This documentation does not authorize outreach.
Discover the target
- Discover list id through lists.list, then read lists.get in the selected workspace and inspect archivedAt. Retain the current name, description and active membership state before planning.
- Read contacts.list with listId and follow its cursors to review intended active members. Retain the exact contact ids and prior membership state.
topolo whoami --json
topolo actions get app_topolo_engage.lists.members.remove --json
topolo workspaces list topolo-engage --json
Input fields
| Field | Required | Meaning and constraints |
|---|---|---|
id | Yes | Required identifier discovered or retained in the same authorized workspace, trimmed non-empty string up to 128 characters. |
contactIds | Yes | Required non-empty array of at most 500 discovered contact identifiers, each trimmed 1–128 characters. Bind the list and all contacts to the same selected workspace; duplicate ids are deduplicated. |
Validate, plan and execute
Set ENGAGE_WORKSPACE_ID to the identifier of your selected workspace. Prepare payload.json from the current input schema and discovered identifiers. Use the same payload for validation, planning and execution.
: "${ENGAGE_WORKSPACE_ID:?Set ENGAGE_WORKSPACE_ID to your selected workspace identifier}"
topolo actions validate app_topolo_engage.lists.members.remove --data @payload.json --json
topolo actions plan app_topolo_engage.lists.members.remove --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --confirm --json
Review the plan and obtain approval for the exact effects before executing the unchanged request.
: "${ENGAGE_WORKSPACE_ID:?Set ENGAGE_WORKSPACE_ID to your selected workspace identifier}"
topolo actions call app_topolo_engage.lists.members.remove --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --confirm --json
For MCP, use topolo_get_action, topolo_validate_action, topolo_plan_action and topolo_call_action. Pass the selected resource context separately; confirm:true is required for mutations.
Worked requests and responses
Captured lists.members.remove · HTTP 200
Action input
{
"id": "list_1",
"contactIds": [
"contact_1",
"contact_9"
]
}
Actual local response
{
"list": {
"id": "list_1",
"name": "Founders",
"description": null,
"memberCount": 0,
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
},
"removed": 1
}
Source test: audience routes > lists > adds and removes members, counting only active contacts. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Effects and response meaning
- Deletes only matching membership rows in the selected list/workspace. The contacts themselves remain. Returns list plus removed, the actual count deleted, which can be zero for absent memberships.
- Archived lists are refused. Deletions run in chunks of 80 ids, so the full request is not a single atomic transaction. This action does not cancel an enrollment or recall outreach.
Verify the result
- Read contacts.list with listId and follow all nextCursor values to confirm the intended active memberships are absent. Use contacts.get to confirm retained contact records; removed is a write count, not a deletion of contacts.
Recover
After an uncertain response, read current memberships and reconcile the intended ids before retrying. An unchanged removal ignores absent memberships, but could remove a concurrently re-added membership, so review current intent first. Re-adding exactly reviewed prior members is a separately approved action and requires active contacts and an active list.
Refused responses
Captured lists.members.remove · HTTP 409
Action input
{
"id": "list_1",
"contactIds": [
"contact_1"
]
}
Actual local response
{
"error": {
"code": "state_conflict",
"message": "Archived lists cannot be changed."
}
}
Source test: audience routes > lists > creates, lists, edits and archives a list. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured lists.members.remove · HTTP 404
Action input
{
"id": "list_1",
"contactIds": [
"contact_1"
]
}
Actual local response
{
"error": {
"code": "not_found",
"message": "List was not found in this workspace."
}
}
Source test: audience routes > lists > keeps lists and memberships inside their workspace. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Error recovery
- HTTP 403
permission_denied: Stop and inspect the permission required by this action in the current credential. Do not broaden permissions or switch organization automatically. - HTTP 422
invalid_action_input: Correct the request against the current input schema and validate it again before planning. - HTTP 409
state_conflict: Read the exact retained contact or list and inspect archivedAt. Archived records cannot be edited or have list memberships changed; repeated archive is refused. Do not invent an unarchive operation. - HTTP 404
not_found: Rediscover the identifier in the same authorized workspace. An identifier from another workspace is not a valid substitute; retain archived identifiers for direct reads.
Related actions
Evidence and limits
-
contracts/audience.mjs -
contracts/audience-filter.mjs -
apps/api/src/audience/audience-routes.ts -
apps/api/src/audience/contacts-handlers.ts -
apps/api/src/audience/contact-search.ts -
apps/api/src/audience/contacts-repository.ts -
apps/api/src/audience/lists-handlers.ts -
apps/api/src/audience/lists-repository.ts -
apps/api/src/audience/filter-source.ts -
apps/api/test/audience-routes.test.ts -
apps/api/test/segments-routes.test.ts -
Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
-
Widget/bootstrap use actual app handler but mocked worker runtime and app identity.
-
No gateway execution, live permissions, external delivery or deployed proof.
The examples are local route captures with synthetic context and simulated providers. They do not prove live authorization, CLI/MCP gateway execution, production state or delivery.